====== Instruction decoder and controller ======
An Instruction Decoder is a circuit that processors implement in order to interpret instructions coming from memory. In synchronous designs, this circuit feeds the appropriate operands into the datapath of the processor, according to the instruction. Additionally, it must communicate with a Controller, whose role is to direct the flow of the execution at every stage of the pipeline.
In this exercise we are interested in implementing the instruction decoder and controller for a small asynchronous processor. While these are often different circuits, in this case the simplicity of the processor allows us to incorporate all the functionality into a single module. From now on, in this exercise, we will refer to this combined module simply as Decoder.
The following figure shows a block diagram of the relevant processor modules.
{{ instruction_decoder-diagram.svg?500, nolink }}
The diagram contains 6 blocks. Your task is to implement the block labelled as //DEC//. All the input and output signals for //DEC// are colored in red and blue, respectively. Signals in black do not interact with //DEC// and are shown only to provide context.
The different modules operate in the following way:
* The instruction controller //INST// loads an instruction from memory and sets it into the instruction bus. When the bus is stabilised, it initiates a decoding request by firing the signal ''dec''. After the instruction has been executed, as indicated by the ''dec_ack'' signal, this module requests a new address from the //IPC// module and loads it from memory.
* The data memory //DATA MEM// has an address port ''@'' obtained directly from the instruction bus. It supports two types of operations, read and write, that are requested with the signals ''mr'' and ''mw'', respectively. After a read operation is finished, //DATA MEM// will set the memory value from the address in ''@'' into the read port ''r'' and enable ''mr_ack''. Write operations are similarly handled with the signals ''mw'' and ''mw_ack''. In this case, the data to write must be stable before ''mw'' is enabled.
* The register block //REG// contains a small number of registers that can store values to perform arithmetic operations. It contains two read ports, ''a'' and ''b'', and one write port ''d''. Like in the case of //DATA MEM//, the port addresses are obtained directly from the instruction bus. The signals ''rgr'' and ''rgw'' are used to request read and write operations, respectively. The signals ''rgr_ack'' and ''rgw_ack'' acknowledge these operations in the same way as in the //DATA MEM// module.
* The //ALU// module performs arithmetic operations. It decides what operation to execute according to the ''op'' port that comes from the instruction bus. It has three ports for its operands: ''imm'', ''a'' and ''b''. The first port allows the //ALU// to obtain values directly from the instruction bus, while ''a'' and ''b'' must be read from the register. The signal ''alu'' informs the //ALU// that the operands are set and an operation can take place. The result of this operation is placed in the output port ''d''. After an operation is complete, the signal ''alu_ack'' is enabled. Furthermore, a signal ''z'' is enabled if the result of the operation was //zero//. This value is guaranteed to remain stable until after the following operation is requested.
* The //IPC// module stands for Instruction Process Counter, and its role is to keep track of the address for the instruction memory. In general, it keeps a copy of the last address loaded and increases it by one when the instruction controller requests a new address. It is also possible to request the module to add an //offset// different than one to the next address by enabling the signal ''jmp''. The offset is obtained from the port ''imp_ofs'' directly from the instruction bus.
The processor implements four instructions that must be decoded and controlled by //DEC//:
* **Arithmetic operation**. Performs an arithmetic operation and stores the result in the register. Corresponds to the code ''op0 = 0'', ''op1 = 0''.
* **Branch operation**. If the result of the last arithmetic operation is a ''0'', adds an offset into the next instruction address. Corresponds to the code ''op0 = 0'', ''op1 = 1''.
* **Load from memory**. Reads from memory and stores the value into the register. Corresponds to the code ''op0 = 1'', ''op1 = 0''.
* **Store in memory**. Stores a value from the register into memory. Corresponds to the code ''op0 = 1'', ''op1 = 1''.
===== Exercise 1: Arithmetic Instructions =====
The first instruction you have to implement is the arithmetic instruction. But before that, it is always a good idea with WTG to first set the basic structure of the controller. This not only makes it easier to approach the problem, but also allows Workcraft to infer signal values when possible.
Let us first define the initialization and leave a waveform for every instruction. Remember to rename every waveform with the instruction it intends to solve.
++++ Detailed instructions|
* Place a state and double click on it to set it as initial.
* Add a new waveform, rename it //initialisation// and add a connection from the initial state. A signal cannot be ''unstable'' in the first initial state. We will assume that, in the initialization of this circuit, the signals ''op0'' and ''op1'' are ''low'' before becoming ''unstable''.
* Click on the signal tool and hold Shift while clicking twice to add two input signals.
* Rename them ''op0'' and ''op1''.
* Double click on them while holding Shift and Ctrl to add a ''destabilise'' transition.
* Double click on them again to add ''stabilise'' transitions.
* Add a new state and four waveforms. Add a connection between //initialisation// and the state, and from the state to the new waveforms.
* Rename each waveform with one of the instructions: //arithmetic//, //branch//, //load// and //store//.
* Set a guard for each waveform depending on the operation it implements:
* Click on the //arithmetic// waveform and set, in the //guard// field on the property editor, the guard ''!op0, !op1''.
* Click on the //branch// waveform and set, in the //guard// field on the property editor, the guard ''!op0, op1''.
* Click on the //load// waveform and set, in the //guard// field on the property editor, the guard ''op0, !op1''.
* Click on the //store// waveform and set, in the //guard// field on the property editor the guard, ''op0, op1''.
++++
The WTG should look similar to the following figure.
{{ instruction_decoder-wtg_structure.svg?44%,nolink }}
You can now implement the arithmetic instruction controller.
++++ Detailed instructions|
* Double click on the //arithmetic// waveform to move to the waveform level.
* Use the property editor to declare the signals ''op0'' and ''op1'' by clicking on their checkbox.
* This instruction requires interactions with the modules //INST//, //REG// and //ALU//. Add all the signals used to interact with them:
* Add the input signals ''dec'', ''rgr_ack'', ''rgw_ack'' and ''alu_ack''. The signal ''z'' is not needed for this instruction and so it can be ignored.
* Add the output signals ''dec_ack'', ''rgr'', ''rgw'', ''alu''.
* Remember that signals can be reorganized by holding Shift while dragging them vertically.
* The signal ''dec'' indicates the start of this scenario. First, the operands must be loaded from the register into the //ALU//:
* Add a ''rise'' transition for signals ''dec'' and ''rgr'' by double clicking on them.
* Add a connection from the transition of ''dec'' to the transition of ''rgr''
* When the register is done loading the values in its output ports, it will ''rise'' the ''rgr_ack'' signal. We can then request //ALU// to perform the operation.
* Add ''rise'' transitions to the signals ''rgr_ack'' and ''alu''.
* Connect the transition of ''rgr'' to the transition of ''rgr_ack'', and the transition from ''rgr_ack'' to the transition of ''alu''.
* The //ALU// will ''rise'' the signal ''alu_ack'' after the operation is performed. The register can then be requested to write the result.
* Add new ''rise'' transitions for signals ''alu_ack'' and ''rgw''.
* Connect the transition of ''alu'' to the transition of ''alu_ack''.
* Connect the transition of ''alu_ack'' to the transition of ''rgw''.
* After the register has finished storing the result of the operation, the //ALU// can stop holding the value and drop the request to write in the register. This can be done in parallel.
* Connect the transition from ''rgw'' to a new transition for signal ''rgw_ack''.
* Add ''fall'' transitions for signals ''rgw'' and ''alu''.
* Connect the transition for signal ''rgw_ack'' to the ''fall'' transitions of ''rgw'' and ''alu''.
* When the //ALU// acknowledges the end of the operation, we can drop the request to read from the register.
* Add a ''fall'' transition for signals ''alu_ack'' and ''rgr''.
* Connect the ''fall'' transition of signal ''alu'' to the ''fall'' transition of ''alu_ack'', and the ''fall'' transition of ''alu_ack'' to the ''fall'' transition of ''rgr''.
* After the register acknowledges the end of the read and write operations, the decoder informs that the operation has been finished.
* Add a ''fall'' transition for signals ''rgr_ack'' and ''rgw_ack'' and a ''rise'' transition for signal ''dec_ack''.
* Connect the ''fall'' transition of ''rgr'' to the ''fall'' transition of ''rgr_ack''.
* Connect the ''fall'' transition of ''rgw'' to the ''fall'' transition of ''rgw_ack''.
* Connect the ''fall'' transition of ''rgr_ack'' and ''rgw_ack'' to the ''rise'' transition of ''dec_ack''.
* Since the operation is finished, the signals ''op0'' and ''op1'' might change value and so they should be considered ''unstable''. In parallel, ''dec'' acknowledges the finalization of the operation.
* Add ''destabilise'' transitions to signals ''op0'' and ''op1''.
* Add a ''fall'' transition to signal ''dec''.
* Connect the ''rise'' transition from signal ''dec_ack'' to the transitions you just added.
* Add a ''fall'' transition for signal ''dec_ack''.
* Connect the ''fall'' transition of signal ''dec'' to the ''fall'' transition of ''dec_ack''.
* At some point before the end of the waveform, the signals op0 and op1 must become ''stable''.
* Double click the signals ''op0'' and ''op1'' to add a ''stabilise'' transition.
* The instruction is now complete and the decoder waits for the next instruction.
* At the waveform level, add a connection from //arithmetic// to the previous nodal state.
++++
The implementation for the first signal can be seen in the next figure.
{{ instruction_decoder-arithmetic.svg?44%,nolink }}
===== Exercise 2: Branch Instructions =====
The next instruction we will implement is the branching instruction. This instruction allows the processor to alter the execution flow depending on logic conditions. Any operation performed in the //ALU// will set the signal ''z'' to ''high'' if the result was ''zero''. It will set ''low'' otherwise. By using this signal we can determine whether to branch or not.
This instruction will need to use //guards//. Implement structure of the WTG for the branch instruction and the common waveform before the //guard//. Keep in mind that ''z'' has to be initialized in a similar way to ''op0'' and ''op1''. The value of this signal is only relevant to the branch operation, so it should remain ''unstable'' in every waveform and become ''stable'' when we need it.
++++ Detailed instructions|
* The signal ''z'' is ''unstable'' in most waveforms, but it first needs to be initialised with a known value.
* Double click the //initialisation// waveform and add a new input signal ''z''.
* Add a ''destabilise'' transition by double clicking ''z'' while holding Shift and Ctrl.
* A waveform with a guard must always declare the signals that are guarded.
* Double click the //branch// waveform and declare the signals ''op0'' and ''op1'' in the property editor.
* The signal ''z'' is necessary to determine whether branching occurs. This signal must become ''stable'' before taking a decision.
* Use the property editor to declare ''z''.
* Double click on ''z'' to add a ''stabilise'' transition.
* The model must now decide what to do depending on the value of ''z''.
* Go to the state level by double clicking outside of the waveform.
* Place a new state and a connection between //branch// and the state.
* Place two new waveforms and rename them //no_jmp// and //jmp//.
* Add a connection from the new state to the new waveforms.
* Click on //no_jmp// and, in the property editor, set the //Guard// field to ''!z''.
* Click on //jmp// and, in the property editor, set the //Guard// field to ''z''.
++++
The resulting WTG can be seen in the following figure.
{{ instruction_decoder-branch_structure.svg?44%,nolink }}
The simplest scenario occurs when ''z'' is ''low''. In this case, there is nothing to do and //DEC// immediately acknowledges the instruction after the decoding request. Implement the waveform for this scenario.
++++ Detailed instructions|
* Declare the signals that are needed to process this scenario.
* In the property editor, use the //declare// button for the signals ''z'', ''dec'', ''op0'', ''op1'' and ''dec_ack''.
* The first transition must be the request for the operation. This is immediately acknowledged in this scenario, since there is nothing to be done.
* Add a ''rise'' transition for signals ''dec'' and ''dec_ack''.
* Add a connection from the transition of ''dec'' to the transition of ''dec_ack''.
* Signals ''z'', ''op0'' and ''op1'' are no longer needed and can become ''unstable''.
* Add an ''unstable'' transition to signals ''z'', ''op0'' and ''op1''.
* Connect the transition from ''dec_ack'' to the transitions of ''z'', ''op0'' and ''op1''.
* Add a ''stabilise'' transition to signals ''op0'' and ''op1''.
* Finally, the request for decoding and the acknowledge can be set to ''low''.
* Add a ''fall'' transition for signal ''dec''.
* Connect the ''rise'' transition of ''dec_ack'' to the ''fall'' transition of ''dec''.
* Add a ''fall'' transition for signal ''dec_ack''.
* Connect the ''fall'' transition of signal ''dec'' to the ''fall'' transition of signal ''dec_ack''.
* The model returns to its waiting state until a new request arrives.
* At the state level, add a connection between //no_jmp// and the state before the guards.
++++
You can see the implementation of //no_jmp// in the following figure.
{{ instruction_decoder-branch_no_jmp.svg?44%,nolink }}
Finally, implement the last waveform for the branch instruction.
++++ Detailed instructions|
* Declare the signals that are needed to process this scenario.
* In the property editor, use the //declare// button for the signals ''z'', ''dec'', ''op0'', ''op1'' and ''dec_ack''.
* Use the signal tool to declare ''jmp'' and ''ipc_ack''.
* The first transition must be the request for the operation. This follows with a request to the //IPC// to branch the address.
* Add a ''rise'' transition for signals ''dec'' and ''jmp''.
* Add a connection from the transition of ''dec'' to the transition of ''jmp''.
* When the transaction with //IPC// is over, //DEC// signals the end of the instruction.
* Add a ''rise'' transition to signal ''ipc_ack''.
* Connect the transition of ''jmp'' to the transition of ''ipc_ack''.
* Add ''fall'' transitions to signals ''jmp'' and ''ipc_ack''.
* Connect the ''rise'' transition of ''ipc_ack'' to the ''fall'' transition of ''jmp'' and the ''fall'' transition of ''jmp'' to the ''fall'' transition of ''ipc_ack''.
* Add a ''rise'' transition to signal ''dec_ack''.
* Connect the ''fall'' transition of ''ipc_ack'' to the ''rise'' transition of ''dec_ack''.
* Proceed like in the //no_jmp// waveform to finish this waveform.
* Add an ''unstable'' transition to signals ''z'', ''op0'' and ''op1''.
* Add a ''stabilise'' transition to signals ''op0'' and ''op1''.
* Add a ''fall'' transition for signal ''dec''.
* Connect the transition from ''dec_ack'' to the transitions of ''z'', ''op0'', ''op1'' and ''dec''.
* Add a ''fall'' transition for signal ''dec_ack''.
* Connect the ''fall'' transition of signal ''dec'' to the ''fall'' transition of signal ''dec_ack''.
* The model returns to its waiting state until a new request arrives.
* At the state level, add a connection between //jmp// and the state before the guard
++++
The next figure shows a complete implementation of the branch instruction.
{{ instruction_decoder-branch.svg?44%,nolink }}
===== Exercise 3: Simplify the model =====
By looking at our current design, you can observe that the instructions always finish in the same way. This is due to the protocol between //DEC// and //INST//. In this exercise, your task is to identify the common parts and create a new waveform called //instruction_finished// that produces the necessary transitions between completing instruction and waiting for the next.
++++ Detailed instructions|
* Modify the structure to accommodate for a waveform after the instructions.
* Remove the connections that go from the instruction waveforms to the nodal state.
* Add a new nodal state and connect the waveforms //arithmetic//, //no_jmp// and //jmp// to it.
* Add a new waveform and a connection from the previous nodal state to this new waveform. Rename the waveform //instruction_finished//.
* Add a connection from //instruction_finished// to the nodal state preceding the instruction waveforms.
* Remove the common transitions from the instruction waveforms.
* Double click on the waveform //arithmetic//:
* Click on the ''fall'' transition for signal ''dec''.
* In the property editor, change the //Direction// to ''raise''. This will remove the transition and the connections to and from it.
* Follow the same procedure to remove the ''fall'' transition for ''dec_ack'' and the ''stabilise'' transitions for ''op0'' and ''op1''.
* Repeat the same procedure for the waveforms //no_jmp// and //jmp//.
* Add the removed transitions to the //instruction_finished// waveform.
* Double click on the //instruction_finished// waveform.
* Declare the signals ''op0'', ''op1'', ''dec'' and ''dec_ack'' by using the //declare// option in the property panel.
* Add a ''fall'' transition to ''dec'' and ''dec_ack''.
* Connect the transition of ''dec'' to the transition of ''dec_ack''.
* Add ''stabilise'' transitions to ''op0'' and ''op1''.
++++
The simplified WTG can be seen in the next figure.
{{ instruction_decoder-simplified.svg?44%,nolink }}
===== Exercise 4: Load and Store Instructions =====
The remaining instructions have to access the //DATA MEM// module for read and write operations. This module uses a similar handshake protocol to the //REG// module. Complete the //DEC// module by adding the Load and Store operations to the WTG.
This exercise intentionally lacks detailed instructions to encourage working on it independnetly. You can compare your solution to the complete WTG of the decoder: {{instruction_decoder.wtg.work|}}
===== Solutions =====
Download all the Workcraft models discussed in this tutorial here:
{{instruction_decoder.zip|All instruction decoder WTGs}}