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.
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:
dec. After the instruction has been executed, as indicated by the
dec_acksignal, this module requests a new address from the IPC module and loads it from memory.
@obtained directly from the instruction bus. It supports two types of operations, read and write, that are requested with the signals
mw, respectively. After a read operation is finished, DATA MEM will set the memory value from the address in
@into the read port
mr_ack. Write operations are similarly handled with the signals
mw_ack. In this case, the data to write must be stable before
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
rgware used to request read and write operations, respectively. The signals
rgw_ackacknowledge these operations in the same way as in the DATA MEM module.
opport that comes from the instruction bus. It has three ports for its operands:
b. The first port allows the ALU to obtain values directly from the instruction bus, while
bmust be read from the register. The signal
aluinforms 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_ackis enabled. Furthermore, a signal
zis enabled if the result of the operation was zero. This value is guaranteed to remain stable until after the following operation is requested.
jmp. The offset is obtained from the port
imp_ofsdirectly from the instruction bus.
The processor implements four instructions that must be decoded and controlled by DEC:
op0 = 0,
op1 = 0.
0, adds an offset into the next instruction address. Corresponds to the code
op0 = 0,
op1 = 1.
op0 = 1,
op1 = 0.
op0 = 1,
op1 = 1.
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.
The WTG should look similar to the following figure.
You can now implement the arithmetic instruction controller.
The implementation for the first signal can be seen in the next figure.
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
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
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.
The resulting WTG can be seen in the following figure.
The simplest scenario occurs when
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.
You can see the implementation of no_jmp in the following figure.
Finally, implement the last waveform for the branch instruction.
The next figure shows a complete implementation of the branch instruction.
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 an instruction and waiting for the next.
The simplified WTG can be seen in the next figure.
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 (21 KiB)
Download all the Workcraft models discussed in this tutorial here:
All instruction decoder WTGs (149 KiB)