CSE 141L Homepage | Lab Overview | Lab Description
| Deadline: | Wednesday May 4th, 1:45pm PT (via moodle) |
| Changes: |
|
While things are looking good now, there is still a lot of work left to be done. Namely, you need to implement the back-end (execution unit) of your processor. We are going to follow a similar path as we did for the front-end; we are first going to design the datapath first (part A), then implement control (part B). Finally, you will do a lot of debugging (to make sure that our customers don't end up with faulty chips) and optimize/evaluate (lab 4). You'll also likely need to go back and change a few things in your front-end now that you have a target ISA. You may also find a few deficiencies in your ISA and will need to properly address them. Based on your previous performance, we know you can accomplish this monumental task though. Good luck!
Jump to: Processor Overview | Core Interface | Front End, Revisted | The BackEnd | Datapath Questions | Coding & Implementation
// D_WIDTH : data width
// PA_WIDTH : port (i.e. IO channel) address width
module core#(parameter D_WIDTH = 34, PA_WIDTH = 4)
(
input clk,
input reset_i,
// I/O interface
input [D_WIDTH-1 : 0] in_data_i,
input in_ack_i,
input out_ack_i
output in_req_o,
output out_req_o,
output [PA_WIDTH-1 : 0] in_addr_o,
output [PA_WIDTH-1 : 0] out_addr_o,
output [D_WIDTH-1 : 0] out_data_o,
);
The core interface is simple - it consists of clk, synchronous reset_i, and I/O interface. When the reset_i signal transitions from high to low, the processor core starts executing from address 0x0 of the instruction memory. The I/O interface provides the interface for in and out instructions, which communicate to the outside world. Since the available pins are usually very limited, the I/O interface multiplexes 16 channels into one physical channel to save the pincount. All I/O interface signals are edge-triggered.
The I/O interface is similar to a conventional bus interface. The 'in' instruction asserts in_req_o and in_addr_o, then waits for in_ack_i to become 1. If in_ack_i arrives, the backend stores in_data_i into the register the 'in' instruction specifies. In the best case, the in_ack_i signal will arrive in the next cycle, but it can be arbitrarily delayed if no data is available in the specified channel. In the waveform example below, the core asserts in_req_o with in_addr_o to 0xF. The core spends two idle cycles, then it stores 0x123 to the register file when in_ack_i signal arrives.

The 'out' instruction works in a similar way, but the backend also asserts out_data_o along with out_req_o and out_addr_o. After asserting a request, the backend waits for out_ack_i. Like in_ack_i, out_ack_i could arrive after an arbitrarily long delay, signaling the completion of a request. In the waveform example below, the core asserts out_req_o with out_addr_o to 0xF and out_data_o to 0x123, then waits for out_ack_i. When the out_ack_i signal is asserted after one cycle, the core completes the out instruction.

| Q1 | Review your fetch unit and update it if you need to. If you make any changes, explain them and why they were needed. |
The following is a list of the major modules that you will need to implement in your execution unit.
A register file is essential to store register data of your processor. In lab 1 you designed your own register file with the following interface:
// NUM_REG : number of registers in the register file
// SEL_WIDTH : number of bits needed to specify a register
// D_WIDTH : data width
module regfile#(parameter NUM_REG = 16, SEL_WIDTH = 4, D_WIDTH = 34)
(
input clk,
input wen_i,
input [SEL_WIDTH-1 : 0] wa_i, // address of register to write to
input [D_WIDTH-1 : 0] wd_i, // data to be written to wa_i register
input [SEL_WIDTH-1 : 0] ra0_i,
input [SEL_WIDTH-1 : 0] ra1_i,
output [D_WIDTH-1 : 0] rd0_o,
output [D_WIDTH-1 : 0] rd1_o,
);

Remember that writing to a register is clocked (i.e. it happens only on the clock edge) while reading from a register can happen at any time. The figure above illustrates the interface for the register file (left).
Depending on your ISA, you may have to modify your register file. For example, if you have an instruction that reads from three registers at once, you would likely need to add another read port.
Your processor has two memory modules - an instruction memory and a data memory. While the instruction memory is a part of the fetch unit, the data memory is located in the backend. You should use the provided wrapper module for the data memory. Like the register file, it allows read and write operations, however it does not support simultaneous read and write in a clock cycle. In one cycle, the data memory can serve either a read operation or a write operation.
Memory Wrapper Module: dmem.v (available here)
module dmem#(parameter A_WIDTH = 13, D_WIDTH = 34)
(
input reset_i,
input clk,
input read_write_req_i,
input write_en_i,
input [A_WIDTH-1 : 0] addr_i,
input [D_WIDTH-1 : 0] din_i,
output [D_WIDTH-1 : 0] dout_o,
output refused_o
);
Data memory interface is straightforward. All the signals of the data memory module are edge-triggered, and you can assert the following signals for read and write operations.
Then, why do we need refused_o signal? refused_o signal indicates that the memory module cannot service the requested operation at that time. There are a few reasons for the asserted refused_o signal; the memory could be being refreshed; or another processor core has already been using the memory in the multi-core system. Although refused_o signal is not used in this lab, your backend must be able to tolerate the refused_o signal; if refused_o is asserted upon a memory request, the backend should request the same operation until it succeeds.

In the waveform above, the core requests one write operation and one read operation. Unfortunately, the data memory cannot service the read operation, asserting refused_o signal. Upon the refused_o signal, the core requests the same operation in the next clock and successfully get the memory value in the second trial.
In addition to the register file and memory module, you would need other components in the datapath of the backend. In the case of the fetch unit, you used a few adders, a sign extender, and a few muxes to get the updated PC. For the backend, it depends on your ISA and how you implement it.
| Q2 | List all the other leaf modules needed for the backend of your processor. For each module, list all the instructions that use the module. |
// I_WIDTH : instruction width
// IA_WIDTH : instruction address width
// D_WIDTH : data width
// PA_WIDTH : port address width
module backend#(parameter I_WIDTH = 17, IA_WIDTH = 12, D_WIDTH = 34, PA_WIDTH = 4)
(
input clk,
input reset_i,
// inputs from the fetch unit
input [I_WIDTH-1 : 0] instruction_data_i,
input [IA_WIDTH-1 : 0] instruction_addr_i,
input instruction_valid_i,
input [I_WIDTH-1 : 0] load_data_i,
input load_data_valid_i,
// outputs to the fetch unit
output dequeue_o,
output restart_o,
output [IA_WIDTH-1 : 0] restart_addr_o,
output load_store_valid_o,
output store_en_o,
output [IA_WIDTH-1 : 0] load_store_addr_o,
output [I_WIDTH-1 : 0] store_data_o,
// I/O interface
output in_req_o,
output out_req_o,
output [PA_WIDTH-1 : 0] in_addr_o,
output [PA_WIDTH-1 : 0] out_addr_o,
input [D_WIDTH-1 : 0] in_data_i,
output [D_WIDTH-1 : 0] out_data_o,
input in_ack_i,
input out_ack_i
);
Using a whiteboard, or some sketch paper, examine each instruction, one by one, and augment your datapath if necessary so that it can handle all of your instructions, just like we did for the single-cycle CPU in 141. In many cases, only a small amount of hardware needs to be added to the existing design to support the additional instruction. After your design has stabilized, then you will want to create a schematic that you can use to communicate your design, and that you can draw on when you figure out the control logic. (Note: Powerpoint or other user-friendly drawing tools are fine. Schematic entries tools are often not worth the trouble, unless they allow you to exactly specify the placement of wires.)
| Q3 | Draw the datapath schematic of the backend, doing your best to mimic the style used in Lab 2's datapath.pdf. Annotate all the relevant control signals as you saw in the fetch unit datapath schematic. Include the datapath schematic in your report. |
For Q4 and Q5, answer questions in the same style used in the following example.
Question: What happens when restart_o and restart_addr_o is asserted? Assume that load_store_valid_o is not asserted.
Answer: Assume that restart_o and restart_addr_o are asserted in cycle 0.
| Q4 | How does your processor handle the following situations?
Draw a path on your frontend and backend datapaths and explain on a cycle-by-cycle basis. Follow the explanation style of the example above. |
| Q5 | For each instruction in your ISA, make a copy of your backend datapath and draw a path in the backend datapath which shows how the instruction is handled, label the settings of the control signals, and give an explanation on a cycle-by-cycle basis. You can group instructions into one drawing if they share the same datapath (e.g. add, sub, and xor will likely all be the same). Again, use the answer style used in the example above. Hint: make sure your datapath has stabilized before you start doing this! |
| Q6 | Implement changes to your register file from Lab 1 to make it compatible with your execution unit. You will not necessarily need to change anything. Describe what changes, if any, you needed to make. |
Unlike for the register file, it is better to use a highly optimized memory generator for the data memory module rather than writing your own data memory module. Fortunately, Altra provides a utility named MegaWizard for such a purpose. Generate a data memory module that has 8K 34-bit words and name is 'dmem_34_8k'. You can follow the step-by-step instructions to do it.
| Q7 | Write a testbench for your memory module, making sure that you use the provided memory module wrapper (dmem.v). Use modelsim to do a behavioral simulation and capture the results of this simulation using one or more screenshots. Include these screenshots in your report. In your report, thoroughly explain both the setup of the testbench the results screenshot(s). The captured screenshot(s) should clearly show that read and write operations work correctly. |
For the following questions, you do not need to provide any writeup in your lab. Simply include all the .v files in the zip that you turn in.
| Q8 | Implement all the other modules required in your datapath. You have to implement each module in a separate .v file with RTL Verilog as you did for modules like adders, muxes, and sign extenders in lab 1. You might find that you can reuse many leaf modules used in the fetch unit. |
| Q9 | Implement backend.v in structural Verilog for the backend of the processor. You do not need to implement the control for the backend in this part of the lab. Since it is hard to test an implementation without control logic, it is enough to show that your Verilog file is synthesizable and implementable. |
| Q10 | Implement core.v as the top module of the your processor design. You can simply wire up the fetch unit and the backend. No control logic is required for core.v. |