CSE141L Lab 2, Part A: Processor Front-End - Fetch Datapath


CSE 141L Homepage | Lab Overview | Lab Description


Deadline: Wednesday, April 13, 1:45pm PT (via moodle)
Changes:
    Lab files updated with proper naming conventions and pushed into a new svn repository!
April 9
    Some parts of the text and schematic were inconsistent with the correct naming scheme. Fixed.
April 9
    Q7 released.
April 11
    "A few additional hints" section added.
April 11
    Tried to make it clearer what is an instruction address and what is the value contained at that address. This was mostly an issue with Q2.

Lab Overview:

Now that you are comfortable with using our design tools, it is time to get down to the business of creating our new microprocessor. Here at AwesomeCore, we split our processor design into two parts: the front-end and the back-end. We'll need more time to strategize and decide on the back-end design so we'll start with the design of the front-end (or fetch unit). Luckily for us, the front- and back- ends are mostly independent so we can immediately start on designing and implementing the fetch unit. In this lab we'll implement the datapath for the fetch unit, saving the control logic for next lab. As was the case with lab 1, you should work alone on this lab. Good luck!

Lab Deliverables:

Because we are giving you the specification for fetch unit datapath, we won't be requiring you to give a demonstration at the end of this lab. The requirements for this lab are:


Lab Description:

Jump to: Lab Files | Fetch Unit Specification | Datapath Implementation

Lab Files:

Start by checking out the svn repository for the lab files. To get the files, do the following in your shell, in the directory you want your files to appear in:

svn checkout svn://parallel.ucsd.edu/fetch-unit-datapath/trunk

This will checkout the trunk directory. You can find all the necessary files with proper naming conventions in this directory. (You may need to install a svn client on your machine.)
svn will allow us to more easily push bug fixes to the class, and is a superior solution to passing zip files around.

Note: You will frequently need to reference this schematic (ppt) for the lab.

The svn repo contains the following files:

File Description
fifo.v The FIFO queue for the project.
adder.v 2-input 32-bit adder verilog module. You must complete the implementation of this module.
fetch.v Verilog module for fetch unit. You must complete the implementation of this module.
mux.v Verilog module for 2-input multiplexor. You must complete the implementation of this module.
signext.v Verilog module for sign extension unit. You must complete the implementation of this module.
ram17_1024b.v Quartus Megawizard-generated Verilog file that describes a RAM with 1024 addresses, each containing 17 bits of data.
lab2a.hex A file that specifies the contents of the RAM. Addresses 0x00 - 0x3f (i.e. 0 - 63) contain their own address (e.g. address 5 have "5" stored in it) while all other addresses (i.e. 64 - 1023) have the value "0" stored in them.
ram17_1024b.qip The Quartus II IP File for the RAM module. It appears this file is only used when you map to an FPGA, but we could be wrong.

Create a new project in Quartus II and add all the Verilog files from the zip file to it. As in lab 1, we will be targeting the Cyclone II EP2C35F672C6 FPGA for our design. Make sure the HEX and QIP files are in your project directory or Quartus might cry.

Fetch Unit Specification

The following code (from the fetch.v file) gives the interface for your fetch unit:

module fetch#(parameter I_WIDTH = 17, A_WIDTH = 10, O_WIDTH = 5)
(
	input	clk,

	// inputs from the exec unit
	input	dequeue_i,
	input	restart_i,
	input	[A_WIDTH-1 : 0]	restart_addr_i,

	// memory interface
	input	load_store_valid_i,
	input	store_en_i, 
	input	[A_WIDTH-1 : 0] load_store_addr_i,    
	input	[I_WIDTH-1 : 0]  store_data_i,        
	output	[I_WIDTH-1 : 0]  load_data_o,
	output	load_data_valid_o,

	// ouputs to the exec unit    
	output	[I_WIDTH-1 : 0]	instruction_data_o,
	output	[A_WIDTH-1 : 0]	instruction_addr_o,
	output	instruction_valid_o
);

Your fetch unit must obey the specification that follows:
  1. An instruction contains 17 bits.
    • The most significant bit (MSB) is the 'P' (or "prediction") bit. If the 'P' bit is set to '0' then the instruction is either: a) not a branch or b) is a branch that has been predicted as 'not taken.'
    • The 5 least significant bits (LSBs) of the instruction are the 'offset' bits for branch instructions. We will use these 'offset' bits to calculate the branch target according to the following formula: target = PC + SignExt(offset). SignExt(offset) is simply the sign extended version of the offset. Note: The offset is given in 2's complement and therefore may contain a negative number. You do not have to worry about the other 11 bits of the instruction for this lab.
  2. SRAM has 1024 (i.e. 1K) addresses and therefore uses 10 bits for addressing. Each address contains 17 bits of data. The implementation of the SRAM (ram17_1024.v) was created using MegaWizard, a utility from Altera, and is highly optimized. Please read the description of the file above to learn about what each address is initialized to in the SRAM.
  3. The FIFO queue has 16 entries, each holding 27 bits of data. The entries of the FIFO are broken into two parts: 1) the address (10 bits) and 2) the instruction (17 bits).
  4. In order to allow the program counter (PC) to restart at an arbitrary address, restart_i and restart_addr_i are provided as inputs to the fetch unit. If restart_i is set to '1' then the PC will change to the address specified by and the FIFO will be cleared. Using this mechanism, we can reset the fetch unit by setting restart_i to '1' and setting restart_addr_i to '0x0.' This will cause the fetch unit to start fetching instructions at address '0'.
  5. To allow reading and writing to the SRAM, the fetch unit supports "load" and "store" instructions. Two inputs to the fetch unit, load_store_valid_i and store_en_i, fully specify load and store instructions. When load_store_valid_i is set to '1', the fetch unit knows the instruction is a load or store. Setting store_en_i to '1' further specifies the instruction as a store (conversely, a '0' value on store_en_i implies a load). For load instructions, the fetch unit produces a 17-bit instruction on the load_data_o output. In the case of a store, the fetch unit writes store_data_i into the address specified by store_addr_i.
A few additional hints:
  1. In the absence of extenuating circumstance (e.g., fifo queue full, restart, or load/store), the fetch unit should fetch one instruction per cycle. Thus, it is a pipelined implementation,that is, it is computing the address of the next instruction at the same time that it is reading out the current instruction from the SRAM, and enqueing onto the FIFO.
  2. Load and store instructions have a higher priority than fetching instructions, and should interrupt fetching for only one cycle. These memory operations receive higher priority because the processor back-end may be dependent on their timely execution. Also, the load/store signals are only a one cycle pulse and would be lost if they are not immediately serviced.
  3. Restart signals only require a one cycle pulse of the restart signals. Thus, like for load/stores, it is important to make sure that your circuit does not "lose" the signal, even if other things like loads/stores are also asserted.

Datapath Implementation

Now that you have the specification for the fetch unit, you need to think about implementing it. Before doing so, you need to take the time to thoroughly understand the specification; we don't want to have any bugs holding us up when we move on to creating the back-end. You should answer the following questions in order to make sure we have all of our bases covered. In the process answering these questions, you will be able to implement the Verilog modules that are needed for a functioning fetch unit datapath. Remember that your answers should be complete but concise. Note: For all questions other than Q5, you should assume that the FIFO never fills up.

Q1 Suppose that the address of the last fetched instruction (pc_r) is 0x12. If the 'P' bit of the last fetched instruction is '0', what is the address of the next fetched instruction? Assume that restart_i signal is not asserted.

The situation described in Q1 requires the use of an adder and MUX. Implement both of these components in Verilog in their respective files (adder.v and mux.v). Make sure you use RTL Verilog.

Q2 What is the binary encoding of an instruction that branches to the instruction located right before the current instruction? For example, if the current instruction address is 0x25, it should branch to address 0x24. Use 'X' for don't care bits. Briefly explain how this case would be handled in the given datapath.

As mentioned earlier, the calculating the branch target requires the sign extended version of the offset. Implement the sign extension module in signext.v so that branches can function correctly. Hint: You can duplicate a bit N times by {N{1'bx}} in verilog. For example, 5'b11111 can be briefly expressed by {5{1'b1}}. )

Q3 Imagine the following scenario. The back-end execution unit has found that the branch prediction in the fetch unit was wrong. How can it generate the correct address for the fetch unit? If the execution unit sent restart_i and restart_addr_i at cycle 10, what is the earliest cycle in which the execution unit can start executing at the correct address? Explain.
Q4 Examine the datapath. Notice that pc_r can be used for the PC and can be fed to the SRAM. Considering that pc_r contains the last fetched PC, it seems odd to use the same address in two consecutive cycles. Explain why this path is needed.
Q5 Envision the following scenario.
  • Cycle 0: An instruction 0x10003 with address 0x10 is fed to the FIFO. (assume that sel_mux[2] is 1 at cycle 0.)
  • Cycle 1: No external input is asserted. However, fifo_full becomes 1 as a result of inserting the instruction with address 0x10 into the FIFO.
  • Cycle 2: load_store_valid_i is asserted with {store_en_i = 0, load_store_addr_i = 0x40}. restart_i signal is not asserted.
  • Cycle 3: No external input is asserted.
  • Cycle 4: The following signals are asserted: {restart_i = 1, restart_addr_i = 0x0, load_store_valid_i = 1, store_en_i = 0, load_store_addr_i = 0x50}.
  • Cycle 5: No external input is asserted.
  • Cycle 6: deque_i signal is asserted.
  • Cycle 7: No external input is asserted. Assume that the 'P' bit of the instruction at address 0x0 is 0.
List all the address values fed to the address port of the SRAM from cycle 0 to cycle 7 with explaination.

You should now be ready to implement your fetch unit in Verilog (fetch.v). In doing so, you should follow these simple guidelines:

  1. Use structural verilog, not a behavioral verilog or RTL verilog. You should instantiate mux, adder, and signext modules in the fetch.v file. The FIFO and RAM have already been instantiated in fetch.v for you..
  2. Use always_ff and non-blocking assignments (i.e. <=) for registers.
  3. Where applicable, always use signal names in the provided datapath schematic. Otherwise, use consistent and readable naming style. The naming convention, proper indentation, and style will be graded. Make sure your Verilog file is readable in the Quartus II editor.
  4. Your Verilog modules should be both synthesizable and implementable in Quartus II.
  5. You may want to peek ahead to Q7 to understand structures in SystemVerilog.
Don't forget to include all your files in the ZIP file you will be turning in.

Q6 Given that you have not implemented control signals yet, why might it make sense to ignore the resource utilization and frequency numbers that Quartus reports?

SystemVerilog Practice

Q7 Click Here For This Part of the Lab.

Good luck!


This lab created by Donghwan Jeon, 2007. Modified by Sat Garcia, 2008. Modified by Arash Arfaee, 2010. MBT 2011.
Notes for 2012: Testbenches should be updated to use @(negedge clk); the testbench style here is copied by students and propagates into future labs.