There exist two ways to view the block diagram:
- Option one is to download BlockDiagram/BlockDiagram.drawio file and open it using the draw.io's desktop application or by opening it on draw.io.
- Option two is to view the BlockDiagram/BlockDiagram.drawio file using Github's website then copy and paste the code into draw.io
- Download the file LogisimCircuit/CircuitProject.circ
- Open the file using Logisim-evolution.
- On a desktop download FPGASources
- Create a new project on Vivado
- Select RTL Project then click next
- Add all files in FPGASources/VerilogSources as sources
- Add all files in FPGASources/Basys3Constraints as constraints
- Select Basys3 Board
- Click Run Synthesis, then click OK
- Click Run Implmentation, then click OK
- Click Generate bitstream, then click OK
- Plug in Basys3
- Click Open Hardware Manager, then click OK
- Then click open target -> Auto Connect
- Click on Program Device, then select your FPGA
- Make sure the circ.bit file is selected then click Program
- The FPGA is now programmed.
- Ahmed Ali
- Omar Elfouly
- Bavly Labieb
- Omar Saleh
Due to the complexity of this project no member ever worked alone. Every commit was done in pairs. A commit by OmarElfouly means it was done by Omar Elfouly and Bavly Labieb, while any push commmited by iRustom was done by Ahmed Ali and Omar Saleh. A general outline of contribution is:
- Demo 1 - Omar and Bavly mainly worked on combinational binary to BCD and magnitude finder, while Ahmed Ali and Omar Saleh worked on unsigned multiplier and display.
- Demo 2 - Omar and Bavly mainly worked on multiplier, display, and circ. Ahmed Ali and Omar Saleh mainly worked on control unit and 7-segment display.
- Demo 3 - All members improved readability of code and applied changes according to comments made by professor Shalan. All members then worked on Report and README.
The following is our Report on our project.
The 2’s complementors take the two binary inputs, and convert them into their magnitude. The MUX chooses whether we take the 8 bits as they are, which is in the case the sign bit is 0, indicating the input is positive, or whether we take the 8 bits 2’s complement, which is in the case the sign bit is 1, indicating that it is negative, so complementing the negative value gives us its magnitude. The selection line is, therefore, the sign bit, if 1, we complement, if 0, we take the input as is.
module magnitudeFinder( circuitInput, magnitude);
input wire [7:0] circuitInput;
output wire [7:0] magnitude;
wire [7:0] twosComp;
assign twosComp = ~circuitInput +1'b1;
assign magnitude = (circuitInput[7]) ? twosComp : circuitInput[7:0];
endmodule
The shift left register is of size 16 bits and takes the multiplicand. If shifting is enabled then on each positive edge of the clock, it shifts 1-bit to the left. The register’s first 8 bits are loaded with the input from the magnitude fixer and the last 8 bits are grounded , such that when the load initial signal is high the register is loaded with its initial values. These control signals are provided by the control unit which guarantees that shifting only occurs after input has been loaded.
The shift right register is of size 8 bits and takes the multiplier. If shifting is enabled then on each positive edge of the clock, it shifts 1-bit to the right. The register’s 8 bits are loaded with the input from the magnitude fixer, such that when the load initial signal is high the register is loaded with its initial values. These control signals are provided by the control unit which guarantees that shifting only occurs after input has been loaded.
Register P can be considered an accumulator that either keeps its value or keeps its value plus the value of the shift left register. The mux chooses whether the input into the product register is 0, or is the accumulated adder which adds the left shift register along with the product. The decision to load is then controlled by b[0] (Which is the least significant bit of the shift right register), such that when b[0] is 1, we load the input into the product register, and when it is 0 we do not load anything. The control signals Li (bar) and LP are such that when the button is pressed, we load 0 into the product, if it is not pressed, then we load only when b[0] == 1.
The control unit takes the buttons and b[0] and the z-flag of the left shift register as inputs. It produces the Li, which is dependent on the BTNC being clicked, and produces LP, which is dependent on BTNC and b[0] to determine whether we load the Register P or not. It also produces the display select, which uses a finite state machine that alternates between three states, and state changes are dependent on BTNR and BTNL, determining which digits are to be displayed (rightmost, middle, leftmost), hence, scrolling through the output product.
module multiplier( clk, inMC, inMP, load_Initial, zeroFlag, LSB_SHRReg, product);
input wire clk;
input wire [7:0] inMC;
input wire [7:0] inMP;
input wire load_Initial;
output wire zeroFlag;
output wire LSB_SHRReg;
output reg [15:0] product;
reg [15:0] SHLReg;
reg [7:0] SHRReg;
reg [15:0] nextp;
initial
begin
SHLReg = 16'b0;
SHRReg = 8'b1;
product = 16'b0;
end
always @(posedge clk)
begin
if(load_Initial)
begin
SHRReg <=inMP;
SHLReg[15:0] <={8'b0,inMC};
product <= 16'b0;
end else
begin
SHLReg <= SHLReg <<1;
SHRReg <= SHRReg >>1;
if(LSB_SHRReg)
begin
product<= SHLReg+product;
end
end
end
assign LSB_SHRReg = SHRReg[0];
assign zeroFlag = ~|SHRReg;
endmodule
module controlUnit(clk,zeroFlag, LSB_SHRReg, buttonRight, buttonCenter, buttonLeft, load_Initial, displayControlSignal, calculatingFlag);
input wire clk;
input wire zeroFlag;
input wire LSB_SHRReg;
input wire buttonRight;
input wire buttonCenter;
input wire buttonLeft;
output reg load_Initial;
output wire [1:0] displayControlSignal;
output reg calculatingFlag;
reg [1:0] displayNextState;
reg [1:0] displayState;
//reg calculatingFlag;
initial
begin
displayState = 2'b01;
calculatingFlag =0;
end
localparam [1:0] right = 2'b01, middle = 2'b10, left = 2'b11;
always @(*)
begin
case(displayState)
right: if(buttonLeft) displayNextState = middle;
else displayNextState = right;
middle: if(buttonLeft) displayNextState = left;
else if(buttonRight) displayNextState = right;
else displayNextState = middle;
left: if(buttonRight) displayNextState = middle;
else displayNextState = left;
default: displayNextState = right;
endcase
end
always @(posedge clk)
begin
if(load_Initial)
displayState <= right;
else
displayState <= displayNextState;
end
always @(posedge clk)
begin
load_Initial <= buttonCenter;
end
always @(posedge clk)
begin
if(buttonCenter)
begin
calculatingFlag <= 1;
end
end
assign displayControlSignal = (calculatingFlag&zeroFlag & ~buttonCenter) ? displayState : 2'b00;
endmodule
The Neg Reg register loads the value of the sign, positive (0) or negative (1), into it, by XORing the sign bit of both inputs. This is necessary because we only want a 1 when exactly one of the inputs is negative and the other is positive, and a 0 if the signs are both the same, which implies an XOR gate. We used a register to store the value of the sign so that when the user is changing their inputs, it does not affect the sign bit on the 7-segment display, and this is done by disabling the load once the initial load of the inputs is complete, and only enabling it on a center button press.
module negativeBoolModule(clk,signBit0,signBit1, load_Initial, negativeProductFlag);
input wire clk;
input wire signBit0;
input wire signBit1;
input wire load_Initial;
output reg negativeProductFlag;
always @(posedge clk) begin
if(load_Initial)
negativeProductFlag <= signBit0 ^ signBit1 ;
end
endmodule
The double dabble function takes in the 16-bit binary product produced by the multiplier and uses combinational logic to convert that input into its 5x4-bit BCD equivalent, which is 20 bits, as each digit occupies 4-bits, and we have a total of 5 digits.
module binaryToBCD (binary, BCD);
input wire [15:0] binary;
output reg [20:0] BCD;
integer i,j;
always @(binary) begin
BCD = 21'b0;
BCD[15:0] = binary;
for(i = 0; i <= 12; i = i+1)
for(j = 0; j <= i/3; j = j+1)
if (BCD[16-i+4*j -: 4] > 4)
BCD[16-i+4*j -: 4] = BCD[16-i+4*j -: 4] + 4'd3;
end
endmodule
The display function takes in the 20 BCD bits, and takes in the display select provided by the control unit, which decides which 3 digits to display on the 7 segment display.
module display( displayControlSignal, bcdProduct, segBCD3, segBCD2, segBCD1);
input wire [1:0]displayControlSignal;
input wire [19:0]bcdProduct;
output reg [3:0] segBCD3;
output reg [3:0] segBCD2;
output reg [3:0] segBCD1;
localparam [1:0] start=2'b00, right=2'b01,middle=2'b10,left=2'b11;
localparam [3:0] underScore = 4'b1111;
always @(*)
begin
case(displayControlSignal)
start: begin
segBCD1 = underScore;
segBCD2 = underScore;
segBCD3 = underScore;
end
right: begin
segBCD1 = bcdProduct[3:0];
segBCD2 = bcdProduct[7:4];
segBCD3 = bcdProduct[11:8];
end
middle: begin
segBCD1 = bcdProduct[7:4];
segBCD2 = bcdProduct[11:8];
segBCD3 = bcdProduct[15:12];
end
left: begin
segBCD1 = bcdProduct[11:8];
segBCD2 = bcdProduct[15:12];
segBCD3 = bcdProduct[19:16];
end
endcase
end
endmodule
The 7-segment function takes the 3 BCD digits, and negative bool then decodes them into their corresponding 7-segment binary bits, which then drive the display on the board, and also produces the negative or positive sign on the board. The function also alternates between the displays on the board at a high enough frequency such that all 4 segments are apparent at the same time to the naked eye. The function also displays “_” in place of the digits before a multiplication takes place.
module SevenSegDisplay(inclk,segBCD3,segBCD2,segBCD1,product,negativeProductFlag,anode_active,segments);
input wire inclk;
input wire [3:0] segBCD3;
input wire [3:0] segBCD2;
input wire [3:0] segBCD1;
input wire [13:0] product;
input wire negativeProductFlag;
output reg [3:0] anode_active;
output reg [6:0] segments;
wire [1:0] toggle;
wire TOGClk;
clockDivider #(50000) TOGClkDiv(.clk(inclk),.rst(reset) ,.clk_out(TOGClk));
wire enOn = 1'b1;
counterModN #(2,4) binCounter2 (.clk(TOGClk),.reset(rst),.en(enOn), .count(toggle));
reg [3:0] numToDisplay;
localparam [3:0] nothing = 4'b1101, negative =4'b1110, underscore = 4'b1111;
always @(*)
begin
case(toggle)
0: numToDisplay <= segBCD1;
1: numToDisplay <= segBCD2;
2: numToDisplay <= segBCD3;
3:begin
if(product==0)numToDisplay <=nothing;// to prevent -0
else numToDisplay <= negativeProductFlag? negative : nothing;
end
endcase
end
always @(*) begin
case(toggle)
2'b00: anode_active = 4'b1110;
2'b01: anode_active = 4'b1101;
2'b10: anode_active = 4'b1011;
2'b11: anode_active = 4'b0111;
endcase
end
always @(*) begin
case(numToDisplay )
0: segments = 7'b0000001;
1: segments = 7'b1001111;
2: segments = 7'b0010010;
3: segments = 7'b0000110;
4: segments = 7'b1001100;
5: segments = 7'b0100100;
6: segments = 7'b0100000;
7: segments = 7'b0001111;
8: segments = 7'b0000000;
9: segments = 7'b0000100;
nothing: segments =7'b1111111;
negative: segments =7'b1111110;
underscore: segments =7'b1110111;
default: segments=7'b1110111;
endcase
end
endmodule
module clockDivider #(parameter n = 5000000)(clk,rst,clk_out);
input wire clk;
input wire rst;
output reg clk_out;
reg [31:0] count;
always @ (posedge clk, posedge rst) begin
if (rst == 1'b1) // Asynchronous Reset
count <= 32'b0;
else if (count == n-1)
count <= 32'b0;
else
count <= count + 1;
end
always @ (posedge clk, posedge rst) begin
if (rst) // Asynchronous Reset
clk_out <= 0;
else if (count == n-1)
clk_out <= ~ clk_out;
end
endmodule
module pushButtonDetector( clk, rst, uncleanInput, cleanOutput);
input wire clk;
input wire rst;
input wire uncleanInput;
output wire cleanOutput;
wire newclk;
clockDivider #(5000) newclkDiv(.clk(clk),.rst(rst) ,.clk_out(newclk));
wire postBounce;
debouncer d(.clk(newclk),.rst(rst),.in(uncleanInput),.out(postBounce));
wire postSynch;
synchronizer s(.clk(newclk),.sig(postBounce),.sig1(postSynch));
risingEdgeDetector r(.clk(newclk), .level(postSynch),.tick(cleanOutput));
endmodule
module debouncer(clk,rst,in,out);
input wire clk;
input wire rst;
input wire in;
output wire out;
reg q1,q2,q3;
always@(posedge clk, posedge rst) begin
if(rst == 1'b1) begin
q1 <= 0;
q2 <= 0;
q3 <= 0;
end
else begin
q1 <= in;
q2 <= q1;
q3 <= q2;
end
end
assign out = (rst) ? 0 : q1&q2&q3;
endmodule
module synchronizer( clk, sig, sig1);
input wire clk;
input wire sig;
output reg sig1;
reg meta;
always @(posedge clk)begin
meta <= sig;
sig1 <= meta;
end
endmodule
module risingEdgeDetector(clk, level, tick);
input wire clk;
input wire level;
output wire tick;
reg [1:0] state, nextState;
reg nextOut;
localparam [1:0] zero=2'b00, positiveEdge=2'b01, one=2'b10;//localparam is not supported by vivado
always @ (level or state)
case (state)
zero: if (level==0) nextState = zero;
else nextState = positiveEdge;
positiveEdge: if (level==0) nextState = zero;
else nextState = one;
one: if (level==0) nextState = zero;
else nextState = one;
default: nextState = zero;
endcase
always @ (posedge clk ) begin
state <= nextState;
end
assign tick = (state==positiveEdge);
endmodule
module counterModN (clk,reset,en,count);
input clk;
input reset;
input en;
output reg [x-1:0] count;
//input clk, reset, en;
//output reg [x-1:0] count;
parameter x=4, n=3;
always @(posedge clk or posedge reset)
begin
if (reset)
begin
count <= 0;
end else if(en)
begin
if(count == n-1)
begin
count <= 0;
end else
begin
count <= count + 1;
end
end
end
endmodule
Above you can see how many LUTs our program uses, which is 238.
The critical path of our circuit is 16.581 ns.
Sequential multiplier could be optimised for speed by using an array of adders. Furthermore, its also possible to optimise for size by using an optimised signed Baugh-Wooley multiplier.
LED0 was an issue due to the multiplication ending too fast which made the led redundant since it turns off and on too fast for the human eye to notice.
A video demo showcasing all our test cases has been recorded and uploaded to the repo. The video demo does the following operations:
- 0 x 0
- 0 x 4
- 0 x -3
- -3 x -5
- 127 x 127
- -128 x -128
- -128 x 4
- -128 x 127
Our program has been flashed onto our FPGA which we will bring with us to present. Please refer to journal.md for updates on our progress through the project.