- Getting Started
- Environment Setup
- Preparing User Modules to Work with the System
- Load Store Unit (LSU) Implementation Guide
- Fetch Instruction Unit (FIU)
- Top Module
- How to test the system
This repository provides a framework for integrating and testing the Bitty Processor and related modules. Follow the instructions below to set up your environment, prepare your modules, and ensure compatibility with the provided system.
- Navigate to this GitHub repository.
- Click the Use this template button to create a new repository.
- Clone the newly created repository:
Replace
git clone <your-repository-url>
<your-repository-url>with the actual URL of your repository.
Ensure you are working in Ubuntu or Windows Subsystem for Linux (WSL) before proceeding.
- Install Verilator and Icarus Verilog:
sudo apt-get install verilator iverilog
- Install Python and Pip:
sudo apt-get install python3 python3-pip
- Install Python requirements:
pip install -r requirements.txt
- Move the Bitty Processor top module and all related modules (ALU, Control Unit, etc.) into the
srcdirectory.
- Ensure the names of your modules match the examples provided below.
If your module names differ, update the top-module accordingly.
- The default top-module design can be found in
bitty-tt-template/src/project_final.v. - You may:
- Add intermediate states to the FSM if additional delays are required.
- Rename the top-module from
tt_um_bittyto a name of your choice.
Here’s a sample of the provided top-module code:
module tt_um_bitty (
input wire [7:0] ui_in, // Dedicated inputs
output wire [7:0] uo_out, // Dedicated outputs
input wire [7:0] uio_in, // IOs: Input path
output wire [7:0] uio_out, // IOs: Output path
output wire [7:0] uio_oe, // IOs: Enable path (0=input, 1=output)
input wire ena, // Enable signal
input wire clk, // Clock signal
input wire rst_n // Active-low reset
);
// Implementation details ...
branch_logic bl_inst();
pc pc_inst();
bitty bitty_inst();
endmoduleYour bitty module must implement the following ports:
module bitty(
input run, // Activate Bitty (high to enable)
input clk,
input reset, // Active-low reset
input [15:0] d_instr, // Instruction input
output [15:0] d_out, // ALU output
output done, // High when instruction is executed
// UART communication ports
input [7:0] rx_data,
input rx_done,
input tx_done,
output tx_en,
output [7:0] tx_data
);Your pc module must implement the following ports:
module pc(
input clk,
input en_pc, // Enable PC
input reset, // Active-low reset
input [7:0] d_in, // Input memory address
output reg [7:0] d_out // Output memory address
);Your branch_logic module must implement the following ports:
module branch_logic (
input [7:0] address,
/* verilator lint_off UNUSED */
input [15:0] instruction,
input [15:0] last_alu_result,
output reg [7:0] new_pc // Updated program counter
);The Load Store Unit (LSU) handles memory operations by interacting with the UART module and managing data transfers to/from memory.
module lsu(
// General ports
input wire clk,
input wire reset, // Active-low reset
output done_out, // Signal indicating the operation is complete
// Design preferences
input [1:0] en_ls, // Load/Store control
input wire [7:0] address, // 8-bit address to be sent
input [15:0] data_to_store,
output [15:0] data_to_load, // 16-bit instruction received
// Ports for UART module
input wire rx_do, // Signal indicating data received
input wire [7:0] rx_data, // Data received from UART
input wire tx_done, // Signal indicating transmission is done
output tx_start_out, // Signal to start UART transmission -> low active
output [7:0] tx_data_out // Data to be transmitted over UART
);- Finite State Machine: You can use Fetch Instruction Unit's FSM as an example to build one for LSU. The code can be found in src/fetch_instruction.v directory. The explanation is provided below.
- Operation Flags: In provided example
en_lsis used to control the load-store operation by Control Unit:00: Idle01: Load10: Store11: Reserved
- Testing: Use the provided testbench
bitty-tt-template/test/new_tb.pyto validate LSU functionality.
The Fetch Instruction Unit (FIU) manages the process of retrieving 16-bit instructions over UART. It sequentially sends a flag byte, an address byte, and receives two bytes (high and low) of the instruction.
- Inputs:
clkandreset: Clock and reset signals.address: Memory address for instruction fetch.rx_do,rx_data,tx_done: UART communication signals.stop_for_rw: Pause FIU during UART operations.
- Outputs:
instruction_out: Fetched 16-bit instruction.tx_start_out,tx_data_out: UART transmission control signals.done_out: Indicates fetch completion.
- IDLE: Initialize and wait for start signal.
- SEND_FLAG: Transmit the operation flag (
0x03). - SEND_ADDR: Transmit the 8-bit address.
- RECEIVE_INST_HIGH: Receive the high byte of the instruction.
- RECEIVE_INST_LOW: Receive the low byte of the instruction.
- DONE: Signal operation completion.
- Ensure all modules conform to the I/O specifications provided above.
- Verilator lint comments (
/* verilator lint_off ... */) are essential for simulation. Do not delete them. - Adjust FSM logic in the top-module if additional delays or states are required for your design.
- Familiarize yourself with the RTL files and test your system thoroughly using the provided environment and tools.
- clk: System clock signal for synchronizing the UART operations.
- rst: Reset signal to initialize the UART module.
- clks_per_bit: Determines the number of clock cycles per UART bit, calculated as
clk_rate/baud_rate. For example, with a 50MHz clock and 9600 baud rate,clks_per_bit = 50,000,000 / 9600 ≈ 5208. - rx_data_bit: Serial input for receiving data bits from external devices.
- rx_done: Indicates when a complete byte has been received.
- tx_data_bit: Serial output for transmitting data bits to external devices.
- data_tx: Parallel data to be transmitted (input).
- tx_en: Enable signal to initiate data transmission.
- tx_done: Indicates when the transmission of a byte is complete.
- recieved_data: Parallel output containing the received byte.
This is a simple 2-to-1 multiplexer that selects one of two inputs based on a control signal (sel).
The top module integrates the Bitty Processor, UART, program counter, and branch logic. It manages data flow and control signals for the entire system.
-
Input/Output (I/O) Ports:
ui_in: Dedicated input signals, e.g.,rx_data_bitandsel_baude_rate.uo_out: Dedicated output signals, e.g.,tx_data_bit.uio_in,uio_out,uio_oe: General-purpose I/O for flexible configuration.
-
UART Integration:
- Configures baud rate dynamically based on
sel_baude_rate. - Handles data transmission (
tx_data_bit) and reception (rx_data_bit) through the UART module.
- Configures baud rate dynamically based on
-
Fetch Instruction Module:
- Fetches instructions from memory and transmits data to external systems via UART.
- Handles instruction decoding and communication with the UART module.
Manages the overall flow of operations with the following states:
- S0: Idle state, waiting for an instruction fetch to complete (
fetch_done). - S1: Transition state after instruction fetch.
- S2: Program counter update.
- S3: Decoding specific instructions (
mem_out[1:0]). - S4/S5: Instruction execution and computation.
- S6: Waits for the Bitty processor to complete its task (
done). - S7: Prepares data for UART transmission.
stop_for_rw: Pauses system operation during data read/write.uart_sel: Selects the UART data source (fetch module or Bitty processor).tx_en,tx_data: Enable and data signals for UART transmission.
This Verilog file serves as a simple testbench for the tt_um_bitty module. It provides a simulation environment to test the functionality of the module.
-
Simulation Configuration:
- The
initialblock sets up signal dumping to a VCD file, which can be viewed in GTKWave for waveform analysis.initial begin $dumpfile("tb.vcd"); $dumpvars(0, tb); end
- The
-
Testbench Signals:
- Declares necessary signals (
clk,rst_n,ena,uio_in, etc.) used to drive and observe the module under test (MUT). - Signals are mapped directly to the DUT (Device Under Test) ports.
- Declares necessary signals (
-
Module Instantiation:
- Instantiates the
tt_um_bittymodule (your DUT) and connects it to the testbench signals. - Includes power signals (
VPWRandVGND) for gate-level testing when theGL_TESTflag is defined.
- Instantiates the
-
I/O Mapping:
- Inputs (
uio_in,ui_in) and outputs (uo_out,uio_out) are connected to the respective ports of the DUT. - The
ena,clk, andrst_nsignals are used to enable, clock, and reset the module during simulation.
- Inputs (
This testbench serves as the skeleton where specific input patterns and stimulus are typically provided by an external cocotb Python script.
This is the corresponding cocotb-based Python testbench file. It provides a more sophisticated simulation setup, leveraging cocotb's features for driving and monitoring the DUT.
-
DUT Ports:
- Maps the DUT signals to Python attributes for ease of interaction:
self.reset = dut.rst_n self.rx_data_bit = dut.ui_in_0 self.tx_data_bit = dut.uo_out_0 self.sel_baude_rate = dut.ui_in_2to1
- The
resetsignal is active-low, meaning it resets the DUT when set to0.
- Maps the DUT signals to Python attributes for ease of interaction:
-
Resetting the DUT:
- The
reset_dutmethod applies and releases the active-low reset while ensuring the initial conditions for other signals:async def reset_dut(self): self.reset.value = 0 self.rx_data_bit.value = 1 self.dut.ui_in_2to1.value = 3 await Timer(100, units="us") self.reset.value = 1
- The
-
UART Simulation:
- Implements UART communication, including transmitting (
send_uart_data) and receiving (transmit_from_tx) bytes over therx_data_bitandtx_data_bitlines.
- Implements UART communication, including transmitting (
-
Shared Memory and Instructions:
- Uses shared memory (
verilog_memory) to synchronize data between the Verilog DUT and an emulator. - Loads an instruction set from a file (
load_instructions) to simulate processor operations.
- Uses shared memory (
-
Flag Processing:
- Processes different flags (
0x01for Load,0x02for Store,0x03for Instruction) received over UART to perform memory and instruction operations.
- Processes different flags (
-
Timeout Handling:
- Implements a 10-minute timeout for the test to prevent indefinite execution:
async def timeout_timer(): await Timer(10 * 60 * 1e9, units="ns") # 10 minutes in nanoseconds raise TimeoutError("Test exceeded the 10-minute limit.")
- Implements a 10-minute timeout for the test to prevent indefinite execution:
-
End-to-End Testing:
- Validates the DUT's functionality by comparing its behavior with an emulator (
BittyEmulator) at each step. - Logs results for every instruction and detects mismatches between the DUT and emulator.
- Validates the DUT's functionality by comparing its behavior with an emulator (
You can create or modify the machine code or assembly instructions based on your testing requirements.
Option 1: Automatic Machine Code Generation
- Use
CIG_run.py:python3 CIG_run.py
- This script generates
output.txt, which contains machine code representing predefined assembly instructions.
- This script generates
Option 2: Writing Custom Instructions
- Manually create or modify the
output.txtfile. - Include your specific assembly instructions for custom testing.
- Convert machine code (
output.txt) into assembly instructions (instructions_for_em.txt) usinger_tool:./er_tool -d -i output.txt -o instructions_for_em.txt
- This ensures the assembly code (
instructions_for_em.txt) is available for the testbench.
- This ensures the assembly code (
- Navigate to the test directory:
cd ~/bitty-tt-template/test
- Execute the testbench using
make:make
- The testbench:
- Loads
instructions_for_em.txt. - Simulates UART communication for instruction execution.
- Compares the outputs of the Device Under Test (DUT) with the expected results.
- Logs the results in
uart_emulator_log.txt.
- Loads
- The testbench:
-
Assembling Assembly Code: If you need to create machine code from
instructions_for_em.txt:./er_tool -a -i instructions_for_em.txt -o output.txt
-
Disassembling Machine Code: To verify or modify
output.txtback into assembly:./er_tool -d -i output.txt -o instructions_for_em.txt
-
Simulated UART Communication:
- UART signals are generated.
- DUT transmissions are captured for validation.
-
Instruction Execution:
- Instructions are fetched from
instructions_for_em.txt. - Execution is simulated in real-time.
- Instructions are fetched from
-
State Validation:
- DUT outputs are compared to expected results.
- Any discrepancies are logged in
uart_emulator_log.txt.
-
Error Reporting:
- Logs mismatches or issues for debugging.
-
Generate Machine Code:
python3 CIG_run.py
-
Disassemble Code:
./er_tool -d -i output.txt -o instructions_for_em.txt
-
Run the Testbench:
cd ~/bitty-tt-template/test make
-
Check Logs:
- Inspect
uart_emulator_log.txtfor results:- Successes and failures.
- Register values.
- Any detected mismatches.
- Inspect
- Edit the info.yaml and update information about your project, paying special attention to the
source_filesandtop_moduleproperties. If you are upgrading an existing Tiny Tapeout project, check out our online info.yaml migration tool. - Edit docs/info.md and add a description of your project.
The GitHub action will automatically build the ASIC files using OpenLane.
- FAQ
- Digital design lessons
- Learn how semiconductors work
- Join the community
- Build your design locally
- Submit your design to the next shuttle.
- Edit this README and explain your design, how it works, and how to test it.
- Share your project on your social network of choice:
- LinkedIn #tinytapeout @TinyTapeout
- Mastodon #tinytapeout @matthewvenn
- X (formerly Twitter) #tinytapeout @tinytapeout
