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


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! If you have not already started then you should use the newer and greater files from here.
April 9
    Q7 released.
April 11
    "A few additional hints" section added.

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 downloading the following zip file:
141L_2a.zip.

Note: You will frequently need to reference datapath.pdf from the ZIP file so we have provided a link to it directly from here.

The zip file 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.
datapath.pdf Schematic of the fetch unit's datapath.
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,
	input	restart,
	input	[A_WIDTH-1 : 0]	restart_addr,

	// memory interface
	input	load_store_valid,
	input	store_en, 
	input	[A_WIDTH-1 : 0] load_store_addr,    
	input	[I_WIDTH-1 : 0]  store_data,        
	output	[I_WIDTH-1 : 0]  load_data,
	output	load_data_valid,

	// ouputs to the exec unit    
	output	[I_WIDTH-1 : 0]	instruction_data,
	output	[A_WIDTH-1 : 0]	instruction_addr,
	output	instruction_valid
);

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' and 'restart_addr' are provided as inputs to the fetch unit. If 'restart' is set to '1' then the PC will change to the address specified by 'restart_addr' and the FIFO will be cleared. Using this mechanism, we can reset the fetch unit by setting 'restart' to '1' and setting 'restart_addr' 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' and 'store_en', fully specify load and store instructions. When 'load_store_valid' is set to '1', the fetch unit know the instruction is a load or store. Setting 'store_en' to '1' further specifies the instruction as a store (conversely, a '0' value on 'store_en' implies a load). For load instructions, the fetch unit produces a 17-bit instruction on the 'load_data' output. In the case of a store, the fetch unit writes 'store_data' into the address specified by 'store_addr'.
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.
  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_prev_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 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 is 0x25, it should branch to 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' and 'restart_addr' at cycle 10, what is the earliest cycle in which the execution unit can start executing at correct address? Explain.
Q4 Examine the datapath. Notice that 'pc_prev_r' can be used for the 'pc' and can be fed to the SRAM. Considering that 'pc_prev_r' saves the "previous 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 instruction 0x10 into the FIFO.
  • Cycle 2: load_store_valid is asserted with {store_en=0, load_store_addr = 0x40}. 'restart' signal is not asserted.
  • Cycle 3: No external input is asserted.
  • Cycle 4: The following signals are asserted - {restart = 1, restart_addr = 0x0, load_store_valid = 1, store_en = 0, load_addr = 0x50}.
  • Cycle 5: No external input is asserted.
  • Cycle 6: 'dequeue' signal is asserted.
  • Cycle 7: No external input is asserted. Assume that the 'p' bit of the instruction at 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 non-blocking assignments (i.e. <=) for flip-flops.
  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.
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.