verilog代写:CSCI 2121- Computer Organization and Assembly Language GROUP PROJECT INFORMATION

CSCI 2121- Computer Organization and Assembly Language GROUP PROJECT INFORMATION

Submission Instructions:

  1. You need to make only one submission per group.
  2. Please list all the group member’s names and the Banner IDs in a separate file.
  3. Save the files as cpu.sv, alu.sv, mem.sv, and mem_controller.sv
  4. Put all files into one folder.
  5. Compress the folder into a .zip file.
  6. Submit the zip file on Brightspace.
  7. Submission Deadline: Sunday, April 15th, 2018, 11.55PM

    SRC CHIP PROJECT

In order to proceed on the project for this course, there are several key Verilog concepts which are necessary in the implementation of this code.

Part 0: More Verilog concepts.

Shared busses and tri-state buffers

Before we begin implementing the CPU, let’s go over a few concepts which will be needed for the lab assignment. The first is the concept of a shared bus. We’ve already seen busses in Verilog in terms of an array of wires or registers, however in the context of our CPU, the word “bus” has another meaning. Our CPU uses a shared “bus” which is a wire connected between the inputs and outputs of many different registers, in order to share data:

This can be easily modelled in Verilog, using the inout keyword for a module. This defines a wire which can act as both an input and an output to our module. However, there is one critically important design concept which goes along with the usage of shared busses:

Only one signal can be written to a shared bus at any given time.

If two signals are written to the bus, the result is a bus collision, and the value is undefined. In the simulation, this is represented as “X”, but in real life this would cause garbage data to exist on the bus, potentially corrupting any process which is running on the CPU. As Verilog programmers, it’s our job to ensure that a bus collision never happens. To facilitate this, we’ll use a digital logic component called a tri-state buffer:

The purpose of a tri-state buffer is to act as a switch. If B is 1, then the data can pass through the buffer, but if not, it simply disconnects the wire, outputting a high-impedance state (in Verilog, denoted by Z).

A

B

C

0

0

Z

0

1

0

1

0

Z

1

1

1

In Verilog, we can’t directly write a tri-state buffer, but it can be easily synthesized using a ternary operator as shown on line 11:

Line 11 shows the construction of a ternary operator to use as a tri-state buffer. If the input called activate_out is set to 1, then the tri-state buffer is activated, and the circuit will put my_result on the bus. Otherwise, it puts the value 32’bz on the bus, which will disconnect my_result from the bus, and allow another circuit to write to the bus.

Note: It is important that during your lab assignment, any circuits which write to the bus have a tri- state buffer implemented to prevent bus collisions.

Verilog Tasks:

Verilog tasks are like object functions in Java. Read more about them here. You may find that they are useful in separating the code for instructions within a module, particularly for the CPU module.

More ways to use the “always” block

We’ve seen the always block in the context of sequential circuits, but it can also be used to define combinational circuits. While we could already defined these blocks with assign, using an always block provides more flexibility, and the ability to write higher level language functions such as if. There are two approaches to writing a combinational circuit with an always block:

In this case, the always block defines what is called a “sensitivity list”, that is, the conditions under which to activate the code. In this case, we activate the code only when a_in is changed. This may seem like a simple “if” statement, but there are more complex issues on how an always block is simulated. If you need multiple always blocks to modify the same signal, you may run into errors which can be difficult to debug. As a result, you may find need for this alternate syntax:

This will trigger when any net in the module is changed. This is important to prevent bugs relating to sensitivity lists with multiple conditions. This concept is essential for completing the lab, because in the SRC chip, all registers are connected to the bus in this way.

2-Dimensional register blocks.

Verilog has useful syntax for defining memory using registers. The following code demonstrates how to make a 2d array of registers to create a block of memory:

One more Verilog concept which may be useful are genvars which can be used, for instance, to perform an assignment on a large set of wires using a for loop. This is useful for defining the tri-state buffers for general purpose registers in the CPU.

Information about the project

The work for this lab can be divided into two streams, according to the following dependency diagram for the modules:

To implement the memory controller and the ALU, it is important to have a good grasp on how tri-state buffers and shared busses work, as detailed above. When it comes to the CPU, it is a good idea to have a look at the testbench code for both the ALU and the memory controller, to get an idea for how to implement the control path logic. In both cases, the testbench code is designed to act as a stand-in for the CPU, and behaves in much the same way.

Part 1: The ALU

The ALU is a relatively straightforward construction in Verilog, if you make use of operators available to Verilog. The ALU has three main components: A register called A, which stores the first operand. A connection to the bus, which will contain the second operand, and a register C, to output the result. Between all of these components is a combinational circuit which will perform the desired operations.

Note that in the following diagram, the star (*) represents a logical subsection of the module, and not another actual module.

Note: There are two slight differences between the above circuit and the ALU for the SRC discussed in the lectures. Firstly, the above circuit does not include the a_out line. It is assumed that the data in the register is automatically available to the arithmetic processing section. Secondly, the shc control line is not implemented since Verilog does not support it as a default operator.

The important part here is how the control logic is performed so that the CPU can make use of it. There are three distinct steps to performing an operation on the ALU (these may vary for certain operations):

  1. A value is loaded into register A using the bus, and the a_in input. Once a_in is set to 1, A should contain the value on the bus.
  2. The value B is loaded onto the bus, to act as the second operand.
  3. The operator line (add, sub, etc.) is set to 1, and the result is stored by setting the c_in input to

    1.

  4. Finally, the bus is cleared and the c_out pin is set to 1.

From here, the CPU will decide what to do with the value which is now active on the bus.

The skeleton code for this module is provided here: https://www.edaplayground.com/x/3aGb Please save this file as alu.sv

Part 2: Memory module

In order to make use of our CPU, we’ll construct a rudimentary memory module. In future lectures, you will learn about more sophisticated techniques for designing memory, but we only need something simple to hold instructions for our CPU. Our memory module will have three main components:

  1. A connection to a shared data bus.
  2. A set of control pins: addr, read, enable
  3. A 65536×32 set of registers.

The chip has the following functionality: Whenever enable goes to 1, we do one of three things:

  1. If read is 1, then the module will output the memory at address addr to the bus. (Read mode)
  2. If read is 0, then the module will write the value from bus to the memory at address addr.

    (Write mode)

Note: There is a slight difference between the above circuit and the memory structure for the SRC discussed in the lectures. We assume that the memory address lines are separate from the data lines and always active. Furthermore, the memory address line is only 16 bits wide rather than 32 bits.

Hint: the syntax for creating a 2d array of registers in Verilog which is 64×16 is as follows:

The skeleton code for this module is provided here: https://www.edaplayground.com/x/36Ze Please save this file as mem.sv

Part 3: Memory Controller

The memory controller is designed to bridge the gap between the CPU’s shared bus, and the memory interface. It is connected to two shared busses: cpu_bus and mem_bus, and it has control pins for its two registers: ma and md. Finally, it is connected to the same read, enable, and address, wires that the CPU will ultimately output to the memory block. The CPU will use the memory controller’s address output as its own address output, so this connects directly to the memory.

This chip has the following behavior:

  1. It has in/out control wires for ma and md. These define behavior for output and input to the cpu_bus
  2. When read is 0 and enable is 1, md will output its value to mem_bus.
  3. When read is 1 and enable is 1, md will take it’s value from mem_bus.
  4. The output address always outputs the content of ma

In order to test this module, you must first complete the memory module, and copy and paste it into the tab called “mem.sv”:

Warning: Do not start this module until you have a working memory module! The unit test relies on testing the controller with a memory module of your construction.

The skeleton code for this module is provided here: https://www.edaplayground.com/x/3PLz Please save this file as mem_controller.sv

Part 4: SRC CPU

This is the most challenging part of the lab. You will implement a CPU which can perform the load, store, add, and subtract operations. The CPU will make use of all of the components we have built so far, and you can add the completed modules to the tabs.

The CPU should consist of a 32-bit pc and ir register, as well as 32 general purpose registers. The goal of this portion of the project is to connect the CPU to the previous module, and using the concrete RTN you learned in the lectures, activate these signals in a specific order (activated by the clock pulse) to execute instructions in memory, starting at address 0x0.

Most of the functionality of the CPU is internal, where its main outside connections are to the memory module. However, for our lab, there is one extra piece of functionality: the reg_select input and reg_value output. Whatever the value of reg_select is, simply output the value in that general purpose register to the reg_value output. This lets the unit test determine what the value in a given register is, after an instruction has been executed. Make sure you implement this behavior before you start unit testing your code.

The CPU module consists of two logical sections:

  1. Control wires and their input/output behavior.
  2. A state machine for executing instructions based on the opcode, the contents of IR, and the

    current state.

In order to determine the behavior for an opcode and a current state, you will have to consult your notes on concrete RTN, and the instruction set specification. For this exercise, we will implement ld, sto, add and sub.

Here is the suggested order for implementing the CPU:

  1. Add all 32 general-purpose registers, as well as PC and IR.
  2. Add the reg_select and reg_value functionality. The unit testing depends on this, so don’t forget

    it.

  3. Create tri-state buffers and combinational logic for input/output to the registers.
  4. Add opcode parsing by creating wires attached to registers in ir.
  5. Add an instance of the SrcALU and SrcMemoryController modules.
  6. Create registers to act as control signals for each of the control wires for SrcAlu and

    SrcMemoryController (1 for every input to each of these modules)

  7. Define a state register to hold the current state of the CPU. The size of this depends on how

    efficiently you can implement each instruction. If you are having a lot of issues with timing, try

    adding more intermediate states.

  8. Create a state-transition diagram for each step of an instruction’s concrete RTN.

9. Using switch or if/else if logic, define the behavior for each instruction. (Note that instruction fetching is the same for all instructions). Each unit test is designed to test a specific instruction, so just try to get the tests passing one at a time. Modify the unit test while working on this to only load one instruction in at a time, if you find the debug output overwhelming.

The skeleton code for this module is provided here: https://www.edaplayground.com/x/4Tuk Please save this file as cpu.sv

The page which follows contains the entire diagram for the SRC CPU.