The 104 Duke_ACC Machine The goal of the next two lessons is to design and simulate a simple accumulator-based processor. The specifications for this processor and some of the QuartusII design components are described in this document. General description: The Duke_ACC processor operates on 8-bit data but utilizes 12-bit instructions. The machine has 256 12-bit wide memory locations for instructions and 256 8-bit wide memory locations for data. (Note: I will sometimes use word to describe instruction or data memory width, keep in mind that this means 12-bits for instructions and 8-bits for data.) The instruction set is limited to ADD, SUB, CLR, LOAD, STORE, BRANCH, and HALT. The BRANCH instruction is an unconditional branch to memory location zero. The HALT instruction stops the system from executing any more instructions. Operations are defined as: ADD Address add the contents of memory location A to the accumulator SUB Address subtract the contents of memory location A from the accumulator CLR clear the accumulator LOAD Address Load the value at memory location A into the accumulator STORE Address Store the accumulator to the memory location A BRANCH branch to instruction memory location zero HALT stop execution Instruction Encoding: Instruction encoding is specified as follows: Opcode Memory Address 11 8 7...0 The opcode is specified using the four high-order bits [11..8] and an immediate memory address is specified in the eight low-order bits [7..0]. The only addressing mode is immediate (i.e., the data memory address comes directly from the instruction bits). OPCODE values: ADD 1100 SUB 1000 CLR 0100 LOAD 0010 STORE 0001 BRANCH 0000 HALT 1111
QuartusII Implementation of Duke_ACC Start a new project in QuartusII. Be sure to set the FPGA to the proper chip ID and import the DE2 pin assignments. Create a new schematic file. Data Path The data path for the Duke_ACC machine is comprised of instruction memory, an accumulator, and data memory. To create the components in this data path we will use the megafunctions within QuartusII. Megafunctions provide parameterized components, thus avoiding the need for us to implement memory and the logic for the accumulator, etc. The following blocks are available as megafunctions. This wizard makes it relatively easy to add sophisticated components to your design. When inserting a megafunction, please be sure the box that says invoke megafunction wizard is checked. We will use the following megafunctions in our data path. Accumulator Megafunctions->arithmetic->altaccumulate: The options you want to include for this less are sload, aclr, add_sub, clken The image below shows what your accumulator should look like. Instruction Memory Megafunctions->storage->altsyncram: 12-bits wide, 256 words, ROM, it should look like the image below. When asked for an initialization file, type in instructionmemory.mif Data Memory Megafunctions->storage->altsyncram: 256 words, 8-bits wide, rdaddr, wraddr, wren, clk. When asked for an initialization file, type in datamemory.mif
Next Address Logic Megafunctions->storage->lpm_dff: 8 flip-flops wide (to hold the PC), include a synchronous clear signal. The image below is what it should look like. Megafunctions->arithmetic->lpm_add_sub: 8-bits wide (for incrementing the PC). Busses: Groups of wires are often called busses, for example the wires that connect the PC D- flipflop with the instruction memory can be drawn as a bus (a single line in the schematic that represents the entire group). Busses are drawn with the thick wire (see left side toolbar) instead of the single thin wire tools. You can specify the label for a bus as an array (e.g., PCval[7..0] would label the bus as having 8 wires numbered from 7 downto 0.) In many cases it is necessary to separate the wires from a bus (group of wires). To accomplish this, you have to draw a split in the bus and label each branch with a subset from the source bus. The diagram below shows how to split a bus called Ibus[11..0] into two one with Ibus[7..0] and the other with all Ibus[11..8]. Then the it again splits out individual wires from the Ibus[11..8]. Wire up the data path Connect the various components of the data path to support the operation of Duke_ACC. Use the bus connectors whenever possible. Designing the control will come next. Connecting the Clock For the data path to function, we need to connect the clock to each component that has a clock input. However, we have to cheat a little to get our system to behave as a single cycle machine. Specifically, the memory components we are using do not match the idealized versions we ve discussed in lecture. Instead they have what are called registered inputs. The address bits are first clocked into registers and then made available to the memory to load/store a value from/to
the memory. Our idealized version treated memory as a combinational circuit for reads---it did not have any storage of address bits within the memory. That means that if we send the same clock signal to the PC and the instruction memory the appropriate instruction will not appear on the output of the instruction memory until after the second clock transition. A similar issue exists for the data memory. To get around this issue and allow our design to behave like a single cycle processor, we will delay the clock signal to instruction memory and the accumulator. This is accomplished by simply sending the clock through an inverter. The original clock signal is connected to the PC flip-flop and the data memory. The output of the inverter is sent to the instruction memory and to the accumulator. We will use a manual clock (a button on the DE2 board). So, insert an input PIN, it is called KEY[0] (caps matter). Do not connect it to anything yet, we will modify the clock based on the HALT instruction and a Reset capability. Reset Key Insert an input pin, call it Key[1] This button will enable us to reset the processor (i.e., after executing a halt instruction. Note, the Keys (buttons) take on value 1 when not pressed and transition to a 0 when pressed. Control The Duke_ACC ISA is relatively simple and the instruction encodings are designed to make control relatively simple. Pay attention to the definitions of the signals. Reset Reset = NOT(Key[1]) Accumulator Control: Add_sub = Ibus[11] AND Ibus[10] Sload = Ibus[9] Clken = Add OR Sub OR Sload = Ibus[11] OR Ibus[9] Aclr = Clr OR Reset = Ibus[9] AND NOT(Ibus[11]) OR Reset Data Memory Control: Wren = Ibus[8] AND NOT(Reset) Program Counter Control: Branch = NOR(Ibus[11..8]) Sclr = branch OR Reset Halt Halt = NAND(Ibus[11..8]) To actually halt the processor we will gate off the clock this is accomplished by the following: Clock = Key[0] AND (Halt OR Reset)
See the attached schematic for a full circuit diagram of Duke_ACC Wire up the entire data path and control for Duke_ACC. Connect the accumulator output to output pins labeled LEDG[7..0] (this will allow display of the accumulator result). Connect the output of the instruction memory to output pins labeled LEDR[11..0] (This will allow display of the current instruction being executed) Memory Initialization Create a new file of type memory initialization (NewFile->Other->memory initialization) called instructionmemory and the type should be mif Select View->Memory Radix->Hexadecimal. Enter the following into this file. Addr +0 +1 +2 +3 +4 +5 +6 +7 00 400 200 801 102 C00 100 F00 00 Save the file (make sure it is of type.mif) Creat a new file of type memory initialization and call it datamemory.mif Enter the following values into this file. Addr +0 +1 +2 +3 +4 +5 +6 +7 00 05 03 00 00 00 00 00 00 These files are used by Quartus to initialize the instruction and data memory. The instructions above correspond to the following program: 0x400 # clr 0x200 # ld mem[0] = 5 0x801 # sub mem[1] = 5-3 = 2 0x102 # st to mem[2] = 2 0xC00 # add mem[0] = 2 + 5 = 7 0x100 # st to mem[0] = 7 0xF00 # HALT Compile the design and create netlist Compile your design (click on the purple play button or Processing->Start Compilation). It will take a while to compile and generate many warnings. After successful compilation, generate the netlist for the functional simulator (Processing->Generate Functional Simulation Netlist) Functional Simulation To see Duke_ACC executing your program we will first use functional simulation.
Create a new Waveform input file (File->Other->Vector Waveform File). Call this file Duke_ACC_input. Add the following pins to the file: Key[0] Key[1] LEDG[7..0] You can add all 7 wires as a single group by just adding the node LEDG. LEDR[11..0] You can add all 12 wires as a single group by just adding the node LEDR For LEDG and LEDR, you should specify in the properties of this bus that it should HEX radix (this will produce a cleaner output waveform). The schematic shows other busses connected to the output pins HEX0[7..0], HEX1[7..0], HEX2[7..0] these are useful for debugging. You can connect them if you want and also add them to the waveform file as busses with a HEX radix. The only two wave forms that you must specify are for KEY[0] and KEY[1]. For KEY[0] you should set it to be a clock. Highlight the KEY[0] entry and then click on the little clock symbol on the left toolbar (it is just above the R). This will open a popup where you can set the time interval to start at 0 and end at 1 us. Set the period to 35ns, offset 0, duty cycle 50%. For KEY[1] set it to a constant value 1 (highlight KEY[1] and then click the 1 on the toolbar) Save the waveform file. Specify functional simulation and your saved file as the input. (Settings->Simulation shows fields for functional simulation and for specifying the input file). You are now ready to simulate the execution of your Duke_ACC. (Processing->Start Simulation) The output wave forms should look like those below: Configuring the DE2 with Duke_ACC After successful functional simulation we are ready to configure the FPGA on the DE2 board with our design for Duke_ACC. Make sure the DE2 board is connected to the laptop and
powered on. Keep the switch in the RUN position. Open the programming window in QuartusII (Tools->Programmer). Click the program/configure box and click start to download the design onto the DE2 board. Executing Duke_ACC We are now ready to execute our program on our Duke_ACC machine. Each time you press the KEY[0] button you are clocking the system. The current instruction is displayed on the Red LEDs LEDR[11..0] while the current value stored in the accumulator is displayed on the Green LEDs (LEDG[7..0]). The first instruction is Clear 0x400, and only LED [10] should be lit up. After pressing KEY[0] again, the next instruction is executed. The next instruction is 0x200 which loads the value from memory location 5 into the accumulator, however the value 5 doesn t show up on the Green LEDs just yet since it doesn t get clocked into the result register until rising edge of the next clock (see the output waveform above). This process continues until you reach the last instruction in our simple program, which is a HALT instruction (0xF00). The HALT instruction prevents anything from happening in our system. Press KEY[0] as much as you want and nothing changes. LEDR[11, 10, 9, 8] are all lit up and the result of our program (7) is displayed on the Green LEDs. To resume execution we have to reset (unhalt) the system. This is accomplished by holding down KEY[1] and pressing KEY[0] once. You should see LEDR[10] lit up. The Reset caused the program counter to reset to zero and the accumulator to clear to zero. Note, memory has not been changed. Therefore, we expect to see different behavior since we have modified data location 0 in the previous execution of our program. Stop holding KEY[1]. Continue clocking the system (pressing KEY[0]) and keep track of the accumulator value. At the end (the next time you see the HALT instruction) you will see that it contains the value 11 (1011) and LEDs 0, 1, 3 are lit up. If you want to start from the beginning again, you can just reprogramming the FPGA again. Changing the program Modify the instruction memory initialization file to replace the HALT with a branch instruction (0x000). First explore the execution using functional simulation. After saving the new instruction memory file update the instruction memory by using Processing->Update Memory Initialization File. Then you can just run the functional simulation again. You should obtain an output wave form like the following.
You can download this new system to the DE2 board and run it by pressing KEY[0] to clock through the execution. Note there is no stopping point, so just stop after the second iteration. Going further. There are two general directions you can now take the Duke_ACC machine: 1) write some programs (modify the instruction memory and the data memory), and 2) extend the design to support more sophisticated branching (e.g., to an arbitrary instruction memory location, or an offset from the PC) and to support conditional branching.