This project implements a custom 16-bit processing system, including hardware modules for program counter (PC), instruction fetch, branch logic, UART communication, and integration with the BittyEmulator for co-simulation. The provided system allows robust testing of a Verilog-based design using a Python-based cocotb testbench. The testbench orchestrates data transfer via UART, interacts with shared memory, and verifies execution against the emulator.
Program Counter (PC):
Instruction Fetch Unit:
Branch Logic:
UART Module:
Bitty Emulator:
instructions_for_em.txt
, loaded by the testbench for execution.
Here’s the revised version written as a description of a fully implemented system:This document outlines the complete instruction set architecture (ISA) for a 16-bit processor, detailing its capabilities, operations, and encoding formats. The ISA is designed to deliver robust functionality for arithmetic, logical, control flow, and memory operations while maintaining a simple, efficient structure.
The processor's instruction set enables dynamic memory interactions, conditional branching, and a wide range of data manipulation tasks, providing the foundation for executing complex algorithms and software applications.
The processor supports both register-to-register and immediate operations, enabling developers to perform computations efficiently.
add rx, ry: Adds the value in ry
to rx
.
rx = rx + ry
sub rx, ry: Subtracts the value in ry
from rx
.
rx = rx - ry
and rx, ry: Performs a bitwise AND between rx
and ry
.
rx = rx & ry
or rx, ry: Performs a bitwise OR between rx
and ry
.
rx = rx | ry
xor rx, ry: Performs a bitwise XOR between rx
and ry
.
rx = rx ^ ry
shl rx, ry: Shifts the bits in rx
left by the number of positions specified in ry
.
rx = rx << ry
shr rx, ry: Shifts the bits in rx
right by the number of positions specified in ry
.
rx = rx >> ry
cmp rx, ry: Compares the values in rx
and ry
.
rx = 0
if rx == ry
rx = 1
if rx > ry
rx = 2
if rx < ry
addi rx, #i: Adds the immediate value #i
to rx
.
rx = rx + #i
subi rx, #i: Subtracts the immediate value #i
from rx
.
rx = rx - #i
andi rx, #i: Performs a bitwise AND between rx
and #i
.
rx = rx & #i
ori rx, #i: Performs a bitwise OR between rx
and #i
.
rx = rx | #i
xori rx, #i: Performs a bitwise XOR between rx
and #i
.
rx = rx ^ #i
shli rx, #i: Shifts rx
left by #i
positions.
rx = rx << #i
shri rx, #i: Shifts rx
right by #i
positions.
rx = rx >> #i
cmpi rx, #i: Compares the value in rx
with the immediate value #i
.
rx = 0
if rx == #i
rx = 1
if rx > #i
rx = 2
if rx < #i
ld rx, (ry): Loads the value from the memory address stored in ry
into register rx
.
rx = mem[ry]
st rx, (ry): Stores the value in register rx
into the memory address stored in ry
.
mem[ry] = rx
ld
or source register for st
.0
for ld
, 1
for st
).11
for memory operations).The processor supports conditional branching with a dedicated encoding format for efficient control flow.
EQ
is set).GT
is set).LT
is set).00
: Equal01
: Greater than10
: Less than10
for conditional branching).Here’s a detailed step-by-step guide for users to set up and test their system with the assembler and testbench:
Before running the testbench, you must first prepare the assembly instructions or machine code. Here’s how:
Option 1: Generate machine code automatically
Run the CIG_run.py
script to generate output.txt
automatically with pre-defined assembly instructions.
python3 CIG_run.py
This will create output.txt
containing machine code.
Option 2: Write custom assembly instructions
If you prefer to write your own instructions, directly create or modify the output.txt
file. These instructions will later be disassembled for further testing.
Disassemble the output.txt
file (machine code) to generate instructions_for_em.txt
(assembly code):
./er_tool -d -i output.txt -o instructions_for_em.txt
This step ensures that the instructions in instructions_for_em.txt
are ready for use in the testbench.
Once you have the instructions_for_em.txt
file ready, navigate to the bitty-tt10/test
directory and execute the testbench using make
:
cd ~/bitty-tt10/test
make
The testbench will:
instructions_for_em.txt
.uart_emulator_log.txt
.To convert instructions_for_em.txt
into machine code (if needed for testing):
./er_tool -a -i instructions_for_em.txt -o output.txt
To convert machine code (output.txt
) back into assembly:
./er_tool -d -i output.txt -o instructions_for_em.txt
Generate Machine Code:
Run CIG_run.py
to create machine code:
python3 CIG_run.py
Disassemble Code:
Use the er_tool
to create instructions_for_em.txt
:
./er_tool -d -i output.txt -o instructions_for_em.txt
Run Testbench:
Navigate to the test directory and run the testbench:
cd ~/bitty-tt10/test
make
uart_emulator_log.txt
.Following these steps ensures smooth operation from writing or generating instructions to verifying the system’s functionality. If you encounter issues, double-check the prepared files or logs for guidance. Let me know if you need further clarification!
Prerequisites:
pip install -r requirements.txt
Input Files:
instructions_for_em.txt
) in the working directory.Shared Libraries:
BittyEmulator.py
and shared_memory.py
are in the project directory.Execute the cocotb testbench:
make
Observe the test results in the terminal and logs:
uart_emulator_log.txt
.This system does not require external hardware. UART communication is emulated within the testbench.
<module_name>.v
: Contains the RTL design files for the system.tb_<module_name>.v
: Top-level Verilog testbench wrapper.test_bitty.py
: The cocotb testbench described above.BittyEmulator.py
: Emulator for reference model validation.shared_memory.py
: Utility for creating shared memory structures.Hardware Expansion:
Error Handling:
Scalability:
This project demonstrates a robust framework for RTL verification, combining software co-simulation with hardware modeling for high-fidelity testing and validation.
# | Input | Output | Bidirectional |
---|---|---|---|
0 | rx_data_bit | tx_data_bit | |
1 | sel_baude_rate[0] | ||
2 | sel_baude_rate[1] | ||
3 | |||
4 | |||
5 | |||
6 | |||
7 |