contributed by <Paintakotako
>
Follow the instructions in Lab3 to install the required dependencies, but i've encounter a situation where Java is not installed, so installtion is needed.
Module
is a built-in Chisel class that all hardware modules must extend.val io = IO(...)
io
valio
and be an IO
objectnew Bundle {...}
in
and out
Passthrogh
modulepoke
expect
expect
statements are true, then the test is passed.true.B
and false.B
are preferred ways to create Chisel Bool literals
Mux
is used to select value, operates like ternary operator
Cat
operator to concatenate to bits value
Ternary operator:
Also known as the conditional operator, is a shorthand way of writing an if-else statement. Its syntax is as follows:
when
describe the behavior of hardwarewhen
does not return value
val result = when(squareIt) { x * x }.otherwise { x }
is not validWire
can serve as an intermediary between two circuits.Wire
such as row10, row11, ...
to be intermediate between input and output.when
vs if
in chisel
when
does not return a value; instead, it is used to describe the behavior of hardware, such as setting signals to specific values or performing certain operations.
if
is not used to control the behavior of hardware; instead, it makes static choices during the generation process. It is typically used for deterministic parameter logic rather than representing hardware behavior.
Reg
holds its output value until the rising edge of its clock, at which time it takes on the value of its input.Reg
has a input in it's prev half clock, and has a output in it's second hald clock.poke
, step
is used to tick the clock once, which will cause the register to pass its input to its output.In previos case, we need to specify Register type, instead, we can use RegNext
, this command will automacitly determine the register type inferred from the register's output connection.
The module has only output and no input; the output of this module is a UInt
with a width of 1 bit
, which means the output can be either 0
or 1
.
CNT_MAX
is a counter register
that contains a value of 24,999,999. The .U
indicates that this value is an unsigned integer
.
cntReg
is a register initialized with 0 as an unsigned integer, with a width of 32 bits
. This means that cntReg can represent a number in the range from 0 to \(2^{32} - 1\).
blkReg
is a register that continuously counts cntReg until its value accumulates to 24,999,999.
Finally, the LED is assigned the value of blkReg
, which is 1
. Then, cntReg
is reset to zero, and it starts accumulating again until it reaches CNT_MAX
. The LED value is then updated to the complement of blkReg (~blkReg)
, and this process repeats.
We can refactor the original code using logic circuits like Mux
with the following pattern:
Refer to the following image for the implementation of a single-cycle machine.
Here we need to determine the next value of the program counter (pc)
based on whether a jump
is required. If a jump is necessary, set the pc to the jump address
; otherwise, set it to pc + 4.
We can inspect the tester's code to examine its poke and expect operations.
It can be inferred that the expected value for jump
is the jump_address_id
, while for non-jump operations, the expected value is the current program counter
.
The following is the result after incorporating the above-mentioned feature:
In the original code, the module defines the following:
The remaining two outputs have not been implemented yet.
Checking the test files in InstructionDecoderTest, we can actually identify a bug. Specifically, there are no tests for the missing two outputs. In other words, filling in random values for the missing two outputs still allows the test to pass.
Here are the output results of the aforementioned behavior.
However, in implementation, we need to determine the instruction type to classify it. If it is of the "load word" type
, then it requires reading from memory, so the memory_read
needs to be set to 1
. Conversely, if it is of the store word
type, as it involves writing to memory, memory_write
should be set to 1
.
Here are the correct results of setting the output after comparing with the opcode.
In the Execute module, two additional modules are declared, namely ALU
and ALUControl
.
Among them, ALU
performs operations based on the values of op1
and op2
, as well as the given func
.
For example, if func is add
, then result = op1 + op2
. Therefore, before entering ALU, it is necessary to specify the function type
to be given to ALU
through alu_ctrl
. After obtaining the function type from alu_ctrl
, the operands (operand1
and operand2
) for ALU
operation need to be specified.
Following the Single-cycle CPU architecture, the missing code for circuit design based on the provided image is as follows.
The assignment of op1
and op2
to ALU
, as well as the corresponding func
, is not completed yet. The func
is obtained from alu_ctrl
, so the alu_funct
of alu_ctrl
is assigned to ALU
. Next, op1
and op2
need to be specified.
The following is the result after incorporating the above-mentioned feature:
Now that the modules for each stage have been defined, the next step is to connect the inputs and outputs for each stage. Once this is done, the single-cycle machine will be complete
Having completed the individual tests mentioned above, we can now execute the test cases.