辅导program编程、讲解Java,c++程序语言、辅导Python编程

- 首页 >> Python编程
Laboratory Exercise 3
The case statement, Adders and ALUs
Revision : 1.3 of September 30, 2020
Simulation Only
This is an exercise in designing multiplexers using the Verilog case statement, using hierarchy
in Verilog, and developing a simple adder and an ALU, where you can learn about the many
Verilog operators.
1 Work Flow
For each part of the lab you should begin by writing and testing Verilog code and compiling
it with Quartus. You should be prepared to show schematics, Verilog, and simulations to
your TA, if requested. You must simulate your circuit with ModelSim using reasonable test
vectors written in the format used in Lab 2 for the simulation files. If you have not found it
already, you should look at the Useful ModelSim Commands handout.
2 Part I
For this part of the lab, you will be learning how to use always blocks (textbook Section
2.10.2, A.11.1) and case statements (text Section 4.6.3, A.11.4) to design a 7-to-1 multiplexer.
Like a module, an always block can have inputs and outputs. A module can contain any
number of always blocks just the same as any module can contain any number of other
module instantiations. The difference is that an always block can only instantiate logic
within the module where it is defined. A module can be instantiated in any other module,
i.e., a module can be reused.
Any output of an always block must have been declared as a reg type in the module containing
the always block.
The model Verilog code for a 7-to-1 multiplexer built using a case statement is shown below.
The seven inputs are from the signals named Input[6:0]. The output is called Out. The
select lines are called MuxSelect[2:0].
1
reg Out; // declare the output signal for the always block
always @(*) // declare always block
begin
case (MuxSelect[2:0]) // start case statement
3’b000: Out = Input[0]; // case 0
3’b001: Out = Input[1]; // case 1
3’b010: Out = Input[2]; // case 2
3’b011: . . . // case 3
3’b100: . . . // case 4
3’b101: . . . // case 5
3’b110: . . . // case 6
default: . . . // default case
endcase
end
An always block is triggered to execute in simulation whenever there is a change in the
sensitivity list. This list is denoted by the asterisk character in the above example. This
means that whenever any input to the always block is changed, the code in the always block
will be simulated. We can change the asterisk to certain inputs to limit when this code is
triggered, but this can lead to simulations that do not match the real hardware. This is
one of the (bad) features of the language. The accepted practice today is to always use the
asterisk in your always block for combinational logic, i.e., any logic where the outputs rely
strictly on the inputs. You will learn more about combinational and sequential logic later.
For now, use the asterisk in the always block for a case statement as shown above.
It is important to have a default case to ensure that all cases are covered. Otherwise, you can
again have simulations that do not match the hardware. Yet another Verilog feature! Your
goal is to write Verilog that will generate hardware that exactly matches the simulation, so
please put in the default statement.
If you want to know why the default statement is important, read on, or else you may skip
this paragraph. When you execute an always block, the use of if and case statements can
take you through different code paths. If you reach the end of the always block and there
is an unassigned (reg) variable, then a memory element, a latch, will be created because
the meaning is that the variable keeps its previous value, so a memory element is inferred.
The problem becomes more subtle because if MuxSelect in the above example is three bits,
there are actually more than eight cases! Each bit can be (1, 0, x (unknown value), z (highimpedance)),
so there are really 43 = 64 possible paths. Synthesis tools will likely assume
only (1,0) and create the correct circuit, but the simulator may not do the same. Always,
always put in the default statement. If you did not understand the above, at least you,
hopefully, now see how Verilog can have subtle side effects that can cause problems. To
avoid these issues, you will be shown the coding patterns that will avoid most problems.
2
2.1 What to Do
The top-level module of your design should have the following declaration:
module part1 (SW, LEDR);
Using SW6−0 as the data inputs and SW9−7 as the select signals, display on LEDR0 the
output of a 7-to-1 multiplexer using the case statement style as shown above.
1. Draw a schematic showing your code structure with all wires, inputs and outputs
clearly labeled. Use switches SW9−7 on the DE1-SoC board as the MuxSelect inputs
and switches SW6−0 as the Input data inputs. Connect the output to LEDR0.
2. After drawing your schematic, write the Verilog code that corresponds to your schematic.
Your Verilog code should use the same names for the wires and instances shown in the
schematic.
3. Create a new Quartus project for your circuit.
4. Include your Verilog file for the circuit in your project.
5. Simulate your circuit with ModelSim for different values of MuxSelect and Input. How
many different cases do you need to simulate to have confidence that your circuit is
working?
6. Compile the project to make sure you can generate a bitstream without errors.
7. Using fake fpga test the functionality of the circuit by toggling the switches and observing
the LEDs. You may want to review what you did in Lab 2 to use fake fpga to
test your design.
3 Part II
Figure 1(a) shows a circuit for a full adder (textbook Section 3.2), which has the inputs
a, b, and ci
, and produces the outputs s and co. Parts (b) and (c) of the figure show a
circuit symbol and truth table for the full adder, which produces the two-bit binary sum
cos = a+b+ci
. Figure 1(d) shows how four instances of this full adder module can be used to
design a circuit that adds two four-bit numbers. This type of circuit is called a ripple-carry
adder because of the way that the carry signals are passed from one full adder to the next.
Write Verilog code that implements the circuit of Figure 1(d) following the steps below. Be
sure to use what you learned about hierarchy in Lab 2.
3
Figure 1: A ripple-carry adder circuit.
4
3.1 What to Do
The top-level module of your design should have the following declaration:
module part2 (SW, LEDR);
1. Draw a schematic showing your code structure with all wires, inputs and outputs
labeled. Use switches SW7−4 and SW3−0 to represent the inputs A and B, respectively.
Use SW8 for the carry-in, cin, of the adder. Connect the outputs of the adder, cout and
S, to the LEDs LEDR9 and LEDR3:0 respectively.
2. Create a new Quartus project for the adder circuit. Write a Verilog module for the full
adder subcircuit and write a top-level Verilog module that instantiates four instances
of this full adder.
3. Simulate your adder with ModelSim for intelligently chosen values of A and B and
cin. Note that as circuits get more complicated, you will not be able to simulate or
test all possible cases. This means that you can test only a subset. Here intelligently
chosen means to find particular corner cases that exercise key aspects of the circuit.
An example would be a pattern that shows that the carry signals are working.
4. Compile the project to make sure you can generate a bitstream without errors.
5. Using fake fpga test the functionality of the circuit by toggling the switches and observing
the LEDs.
4 Part III
Using Parts I and II from this lab and the HEX decoder from Lab 2 Part III, you will
implement a simple Arithmetic Logic Unit (ALU). An ALU has two inputs and can perform
multiple operations on the inputs such as addition, subtraction, logical operations, etc. The
output of the ALU is selected by function bits that specify the function to be performed
by the ALU. The easiest way to build an ALU is to implement all required functions and
connect the outputs of the functions to a multiplexer. Choose the output value for the ALU
using the ALU function inputs to drive the multiplexer select lines. The output of the ALU
will be displayed on the LEDs and HEX displays.
Shown in the pseudo-code, i.e., not exact syntax, case statement below are the operations
to be implemented in the ALU for each function value. The ALU has two 4-bit inputs, A
and B and an 8-bit output, called ALUout[7:0]. Note that in some cases, the output will
not require the full 8 bits so do something reasonable with the extra bits, such as making
them 0 so that the value is still correct.
5
always @(*) // declare always block
begin
case (function) // start case statement
0: A + B using the adder from Part II of this Lab
1: A + B using the Verilog ‘+’ operator
2: Sign extension of B to 8 bits
3: Output 8’b00000001 if at least 1 of the 8 bits in the two inputs is 1
using a single OR operation
4: Output 8’b00000001 if all of the 8 bits in the two inputs are 1 using
a single AND operation
5: Display A in the most significant four bits and B in the lower four bits
default: . . . // default case
endcase
end
4.1 Things to Watch For
Note that in this part of the lab, you will need to learn about number representations
in Verilog (textbook Section A.5) and various other Verilog operators (textbook Section
4.6.5). You will need to learn about several operators including Verilog concatenation for
the additions to stick in the extra bits you need and to create other 8-bit values, and Verilog
reduction operations for ORing and ANDing multiple bits without typing out the operation
for each bit individually.
The case statement must always be inside an always block. If you look at the operations
for the ALU, four of the cases can be implemented with logic expressions, which can be
described within an always block, so they can be expressed within the relevant cases in the
case statement. However, for Case 0 you have to use the adder module you built for Part II.
You cannot instantiate a module within an always block, so how do you make this work? If
you come up with the correct schematic for your circuit, that will tell you what to do.
4.2 What to Do
The A and B inputs connect to switches SW7−4 and SW3−0 respectively. Use KEY2−0 for
the function inputs. Display ALUout[7:0] in binary on LEDR7−0; have HEX0 and HEX2
display the values of B and A respectively and set HEX1 and HEX3 to 0. HEX4 and HEX5
should display ALUout[3:0] and ALUout[7:4] respectively.
The top-level module of your design should have the following declaration:
6
module part3 (SW, KEY, LEDR, HEX0, HEX1, HEX2, HEX3, HEX4, HEX5);
1. Draw a schematic showing your code structure with all wires, inputs and outputs
labeled.
2. Write a Verilog module for the ALU including all inputs and outputs.
3. Create a new Quartus project for your circuit.
4. Simulate your circuit with ModelSim for a variety of input settings, ensuring the output
waveforms are correct.
5. Compile the project to make sure you can generate a bitstream without errors.
6. Using fake fpga test the functionality of the circuit by toggling the switches and observing
the LEDs.
Note: In your simulation, KEY3−0 are inverted. Remember that the DE1-SoC board recognizes
an unpressed pushbutton as a value of 1 and a pressed pushbutton as a 0.
7

站长地图