Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,23 @@ on:

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- name: install dependencies
run: sudo apt install nasm
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install python dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Install iverilog
run: |
sudo apt-get update
sudo apt-get install iverilog
- name: make test
run: make test
- name: make
run: make all
- name: Upload artifacts
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.vscode/
__pycache__/
build/
output/
62 changes: 13 additions & 49 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,58 +1,22 @@
BUILD = build
OUTPUT = output
SIMULATOR_FLAG := -DOPC_SIM_ENABLED
OPC_CFLAGS = -nostdlib -nodefaultlibs
SRC_DIR=.
BUILD_DIR=build
OUTPUT_DIR=output

.PHONY: clean prep all artifacts

all: prep $(BUILD)/greeting $(BUILD)/sim_greeting artifacts
.PHONY: clean test

clean:
rm -f $(BUILD)/*

prep:
mkdir -p $(BUILD)/
mkdir -p $(OUTPUT)/

# simulator specific rules
$(BUILD)/sim_o: lib/sim.c
gcc -c -o $@ $< $(SIMULATOR_FLAG) -I include/

$(BUILD)/sim_asm_o: lib/sim.asm
nasm -f elf64 -o $@ $^

$(BUILD)/sim_logging_o: lib/logging.c
gcc -c -o $@ $< $(SIMULATOR_FLAG) -I include/

$(BUILD)/logging_o: lib/logging.c
gcc -c -o $@ $< $(OPC_CFLAGS) -I include/

$(BUILD)/sim_greeting: $(BUILD)/sim_o $(BUILD)/sim_asm_o $(BUILD)/greeting_o $(BUILD)/font_o $(BUILD)/sim_logging_o
gcc -o $@ $^

$(BUILD)/greeting: linker.ld $(BUILD)/ourpc_asm_o $(BUILD)/greeting_o $(BUILD)/font_o $(BUILD)/logging_o
ld -T linker.ld -o $@ $(BUILD)/ourpc_asm_o $(BUILD)/greeting_o $(BUILD)/font_o $(BUILD)/logging_o

# simulator agnostic rules
$(BUILD)/font_o: lib/font.c
gcc -c -o $@ $(OPC_CFLAGS) $< -I include/

$(BUILD)/ourpc_asm_o: lib/ourpc.asm
nasm -f elf64 -o $@ $^
rm -r $(BUILD_DIR)

$(BUILD)/greeting_o: greetings.c
gcc -c -o $@ $(OPC_CFLAGS) $^ -I include/
include emulator/Makefile.mk

# independent helper tools
$(BUILD)/text_to_led: text_to_led.c $(BUILD)/font_o
gcc -o $@ $^ -I include/
pytest:
pytest -s

# generate artifacts
test: pytest test_verilog_modules

artifacts: $(OUTPUT)/sample_rom_text.txt $(OUTPUT)/objdump_greetings.txt
$(OUTPUT_DIR)/programs/%.bin: programs/%.asm
mkdir -p $(dir $@)
python3 -m planner asm -b $^ > $@

$(OUTPUT)/sample_rom_text.txt: $(BUILD)/text_to_led
$^ "Happy Diwali!" > $@

$(OUTPUT)/objdump_greetings.txt: $(BUILD)/greeting
objdump -D $(BUILD)/greeting > $@
all: $(patsubst programs/%.asm, $(OUTPUT_DIR)/programs/%.bin, $(shell find programs/ -name '*.asm'))
98 changes: 97 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,102 @@

The eventual goal(?) is to build a general-purpose processor integrated with simple input (e.g. buttons) and output devices (8x8 LED display).

## Verification

### Emulation

```
# Build ROM[boot]
$ python3 -rb assembler/assembler.py programs/boot_sequence.asm | tee build/boot_rom.txt.

# Write your program in custom assembly.
$ cat programs/ping_pong.asm # we can proceeding with this

# Use assembler to translate the instructions into machine code.
$ mkdir -p build
$ python3 assembler/assembler.py -r programs/ping_pong.asm | tee build/ping_pong_resolved.txt # optional
$ python3 assembler/assembler.py -rb programs/ping_pong.asm | tee build/ping_pong_rom.txt

# Use emulater the run the machine code.
$ python3 emulator/emulate.py build/ping_pong_rom.txt
```

## Design

TBU
### Specs

* Address Line: 16-bits
* Max Memory: 64KB

### Constants

* INSZ = 0x20, independent input bytes
* OUTSZ = 0x20, independent output bytes
* IPC = 0x0100, intial value of `PC` (or Program Counter).

### Memory Allocation

* `RAM[0:INSZ]` is mapped to I/O module input
* `RAM[INSZ:OUTSZ]` is mapped to I/O module output
* `RAM[IPC:IPC+x]` is loaded from ROM. So it essentially contains `.text`, `.data`.

### Sequencing


* At boot
* Load `ROM[0:x]` into `RAM[IPC:IPC+x]`
* TODO: How?

### Assembly

* `.bss` must be the last section.
* Registers don't really exists. `R[0-7]` are mapped to memory location in `.bss` for convenience and some instructions return response.

### Architecture

#### I/O

Hardware interact asynchronously with IOM (I/O Module) which then interact with RAM at program's will. (WE ARE NOT DOING IT)

* Input devices publish state change in IOM and Output devices read from IOM.
* Program use `IN <index>` instructions to read from `IOM_in[index]` and write to `RAM[index]`. `IOM_in` won't cache input and it will be read as real-time value. If a input state needs to be cached, it's the input device responsibility.
* Program use `OUT <index>` instructions to read `RAM[INSZ+index]` and write to `IOM_out[index]`.



# TODO

## Processor

* Address bits: 8
* Register size: 8
* Memory size: 2**8 = 256 bytes

### Idea

To keep number of component small, we would split a single instruction execution period into 4 cycles.

* Reset
* Set `PC = 0`
* sub-cycle clock to cycle-0
* Cycle 0
* Fetch instruction from `ROM[$PC]` into `pin_INS`
* Cycle 1
* Perform first read

## Assembler

### Details

* Registers: R0, R1, R2, R3 or `R{NUM}`

* Input/Output pin: IO0, IO1, ..., IO7 or `IO{NUM}` (8-bits)

### Instructions

* `IN R{NUM}`: short-blocking input with 8-bit response.
* `OUT R{NUM}`: short-blocking 8-bit output.

## Syntax: High Level

Not yet defined.
11 changes: 11 additions & 0 deletions emulator/Makefile.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
BUILD_EMULATOR = $(BUILD_DIR)/emulator
SRC_EMULATOR = $(SRC_DIR)/emulator

.PHONY: test_verilog_modules

$(BUILD_EMULATOR)/%_test: $(SRC_EMULATOR)/%_test.v
mkdir -p $(dir $@)
iverilog -o $@ $^

test_verilog_modules: $(patsubst $(SRC_EMULATOR)/%_test.v, $(BUILD_EMULATOR)/%_test, $(shell find $(SRC_EMULATOR) -name '*_test.v'))
$(foreach test_name, $^, echo "Executing $(test_name)" && ./$(test_name))
Empty file added emulator/__init__.py
Empty file.
128 changes: 128 additions & 0 deletions emulator/chipset.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
`include "emulator/module/clock.v"

module CHIPSET();
// Global Registers
reg execute_from_brom;

// Stages
wire[0:3] clk;
wire[0:3] is_stage;
CLOCK clock(
.clk(clk[0:3]),
.is_stage(is_stage[0:3]));

// Boot Sequence
//
// When the device have power supply but `is_powered_on` is off. The pc_eval
// forces `program_counter_next` to be 0 and `execute_from_brom` to True.
// If we keep the `is_powered_on` button in off stage for at-least 4 cycles
// then at "stage3 posedge" program_counter should get updated to 0.
// After that for every "stage0 posedge" till is_powered_on is off,
// program_counter:0 along with execute_from_brom:True will be used to pull
// up and execute first instruction from BROM.
//
// Assumption: No stateful IO devices are connected.
// TODO: Implement `execute_from_brom` update implementation.
wire is_powered_on;
BOOT_CONTROL boot_control(.is_powered_on(is_powered_on));


// MBLOCK is a continous circuit and doesn't depend on clock
// but the behaviour do depend on stage which is abstracted.
wire[15:0] program_counter;
wire[1:0] mblock_selector;
wire[15:0] mblock_address;
wire[31:0] mblock_input;
wire[31:0] mblock_output;
wire mblock_write;

MBLOCK_MUX mblock_mux(
.mblock_address(mblock_address[15:0]),
.mblock_selector(mblock_selector[1:0]),
.execute_from_brom(execute_from_brom),
.is_stage(is_stage[0:3]),
.address0(program_counter),
.address1(v0_source),
.address2(v1_source),
.address3(v2_source),
.is_write(4'b0000));

MBLOCK mblock(
.out(mblock_output),
.selector(mblock_selector),
.in(mblock_input),
.address(mblock_address),
.is_write(mblock_write));

// STAGE0

// TODO: Ensure MBLOCK supplies expectations.
// MBLOCK_MUX is expected to fetch MBLOCK at `program_counter` from
// BROM / RAM based on `execute_from_brom` and redirect the value
// to full_ins via mblock_output.

// @stage0 posedge following values should freeze.
wire[7:0] v0_source, v1_source, v2_source, instruction_op;
INS_RESOLVER stage0(
.v0(v0_source), .v1(v1_source), .v2(v2_source), .op(instruction_op),
.full_ins(.mblock_output),
clk[0]);

// STAGE1

// TODO: Breakdown instruction_op into sub-operations

// TODO: Ensure MBLOCK supplies expectations.
// MBLOCK_MUX is expected to fetch MBLOCK based on v0_source and
// instruction_op breakdowns and redirect the value into v0.

// @stage1 posedge following should freeze.
wire[31:0] v0;
FETCH_AND_STORE stage1(
.value(v0),
.in(mblock_output),
.clk(clk[1]));

// STAGE2

// TODO: Ensure MBLOCK supplies expectations.
// MBLOCK_MUX is expected to fetch MBLOCK based on v0_source and
// instruction_op breakdowns and redirect the value into v0.

// @stage2 posedge following should freeze.
wire[31:0] v1;
FETCH_AND_STORE stage2(
.value(v1),
.in(mblock_output),
.clk(clk2));

// STAGE3
// TODO: alu_op should be computed using instruction_op breakdowns.
wire[3:0] alu_op;
wire[31:0] v2;

wire flag_alu_zero;
ALU alu(
.out(v2),
.is_zero(flag_alu_zero),
.op(alu_op),
.in0(v0),
.in1(v1));

// MBLOCK input only comes from ALU output.
assign mblock_input = v2;

// TODO: jump instruction
PC_NEXT pc_next(
.program_counter_next(program_counter_next),
.program_counter(program_counter),
.is_powered_on(is_powered_on));

// @stage3 posedge following should freeze.
wire[15:0] program_counter_next;
flipflop16 pc(
.out(program_counter),
.in(program_counter_next),
.clk(clk3));

endmodule
7 changes: 7 additions & 0 deletions emulator/lib/adder.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module ADDER(
output[15:0] out,
input[15:0] in0,
input[15:0] in1);

assign out = in0+in1;
endmodule
22 changes: 22 additions & 0 deletions emulator/lib/adder_test.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
`include "emulator/lib/adder.v"

module adder_test;
reg[15:0] in0, in1;
wire[15:0] out;

ADDER dut(
.out(out),
.in0(in0),
.in1(in1));

initial begin
in0 = 2536;
in1 = 113;
# 10
$display("ADDER_TEST: in0=%b in1=%b", in0, in1);
if (out !== 2649) begin
$error("latch failed");
$fatal(1);
end
end
endmodule
Loading