In this lab you will be combining the ALU and the Register File (RF) to implement a basic datapath capable of computing a short instruction routine.
In the previous lab, a register file was created from a structural approach. You programmed latch, D-latch, D flip-flop, 4-bit register, and 4-bit register file. This method has benefits in that it becomes very easy to see how many logic gates are required to implement a desired module. However, the time it takes to create such a module is significant, considering that basic edge triggered memory devices are commonly used elements. In this lab, a more behavioral model will be used. The behavioral model is easier to implement, but it is less clear about the low level detail available to a structural model. This additional abstraction helps to simplify designs.
(In the previous lab, you might have used behavioral model and the ‘always’ statement in a small portion of you code. In this lab, the majority of your programs will be based on the behavioral model.)
The ‘always’ statement, although it can be used in a limited sense to describe combinatorial logic, is really meant as a construct to signify event triggered sequential operation. The events can be single signal edge triggered (positive or negative) or they can be more complex and dependent on several signals. It is generally best to limit the dependency. The simplest example below shows an implementation of a positive edge-triggered d-flip-flop. Also note the use of a new data type, ‘reg’, used to explicitly specify a register, as opposed to a simple wire.
module dff(q, d, clk);
input d, clk;
output q;
reg q;
always @(posedge clk)
q = d;
endmodule
Another important note is that the register ‘q’ has the same label as the output ‘q’. There is no name conflict. This means that the output value of this module is ‘registered’. However registers could also be used internally and not connected directly to an output.
The above module works the same as the 1-bit d-flip-flop created in the previous lab. Obviously, this implementation is shorter, but the advantage does not end there. The above module can easily be modified to support a register of any number of bits, without creating a new module and including multiple instantiations of the 1-bit module. Below is a demonstration of the necessary modification.
module dff8(q, d, clk);
input [7:0] d;
input clk;
output [7:0] q;
reg [7:0] q;
always @(posedge clk)
q = d;
endmodule
Notice that the only difference is the bus sizes of the components. The behavioral code does not change. This is one of the strengths of behavior level modeling. The behavior can be abstracted for arbitrary data widths.
There is also no assign statement used in these example modules. The reason for this is that a register could potentially be assigned multiple values (at different times). A standard wire or output, cannot be specified as the result of more than one assign statement or be included as the output of more than one submodule (or as the output of a submodule and the output from an assign statement).
Replace the 4-bit register module in your register file from the previous lab with a behavior level module. Verify your design still works.
Customize your 4-bit ALU (with CLA) into the following module:
module alu(data_in1, data_in2, binv, op, data_out);
You do not have to change your original 4-bit ALU module; instead, let ALU module use an instantiation of the original one and provides inputs carry_in and less and ignore outputs G and P.
Now combine your register file with your 4-bit ALU to form a datapath (uncompleted at this time):
module datapath(drega, dregb, aluout, instruction,
loaddata, clk);
where ‘drega’ and ‘dregb’ correspond to the outputs from your register file, ‘aluout’ is the result of your ALU, ‘instruction’ is an input bit code that controls the datapath specified below, ‘loaddata’ is a data value written directly to the register file, and ‘clk’ is the synchronizing signal used to determine when the register file stores its value.
The ALU requires two inputs, ‘A’ and ‘B’. These inputs will come directly from the register file outputs (which are also output from the datapath module). The output of the ALU is then optionally data input to the register file, as the input to the register file could also come from ‘loaddata’.
The instruction is a 10-bit bus that has the following format:
load(1):ALUop(3):addra(2):addrb(2):addrc(2)
where ‘load’ is a single bit that when high, routes ‘loaddata’ to the register file to be stored in the register addressed by ‘addrc’ and can be used to signal an ‘ldi’ (load immediate) instruction. If ‘load’ is high, the rest of the instruction (other than ‘addrc’) is meaningless. When ‘load’ is low then the ALU is providing the input to the register file.
ALUop is a 3-bit value that is the opcode of the ALU where ALUop[2] is ‘binvert’.
The remaining address fields are passed directly to the register file to indicate which registers are used as inputs to the ALU or as the output from the ALU.
Assume that the register file will be written on every clock cycle and tie the write enable signal of the register file high (1). Later datapath implementations will use a more complicated control unit to manipulate signals such as write enable.
Demonstrate that your datapath works by executing the following instructions translated to the inputs needed for the waveform editor of MaxPlus-II.
ldi $r0, 1
ldi $r1, 3
ldi $r2, 2
add $r3 $r1 $r2
sub $r2 $r3 $r0
or $r0 $r1 $r2
and $r3 $r2 $r1
slt $r2 $r1 $r0
Remember that the first input is the ‘result’ and the second and third inputs are the two operands. Therefore, the slt instruction will put a value of 1 into r2 if (r1) < (r0).