aboutsummaryrefslogtreecommitdiffstats
path: root/atk16_fpga
diff options
context:
space:
mode:
authorJan Tuomi <jan@jantuomi.fi>2025-03-18 18:38:44 +0200
committerJan Tuomi <jan@jantuomi.fi>2025-03-18 18:38:44 +0200
commit69de37cdb25563f6cbd2a3c1a338b1e0dd47b3a4 (patch)
treec1895044fd0051f919a989460be3ac39a6ab09b8 /atk16_fpga
parent06e8b8dbadf769507abf5368caee253ded139adc (diff)
Start reworking fpga impl
Diffstat (limited to 'atk16_fpga')
-rw-r--r--atk16_fpga/.gitignore11
-rw-r--r--atk16_fpga/Makefile24
-rw-r--r--atk16_fpga/alu.v35
-rw-r--r--atk16_fpga/alu_gtkw_translate_sel.txt8
-rw-r--r--atk16_fpga/alu_tb.gtkw54
-rw-r--r--atk16_fpga/alu_tb.v73
-rw-r--r--atk16_fpga/bram.v37
-rw-r--r--atk16_fpga/bram_fsm.v58
-rw-r--r--atk16_fpga/bram_fsm_tb.gtkw32
-rw-r--r--atk16_fpga/bram_fsm_tb.v84
-rw-r--r--atk16_fpga/cu.v524
-rw-r--r--atk16_fpga/cu_tb.gtkw73
-rw-r--r--atk16_fpga/cu_tb.v587
-rw-r--r--atk16_fpga/flash.v60
-rw-r--r--atk16_fpga/flash_to_ram.v27
-rw-r--r--atk16_fpga/foo.v106
-rw-r--r--atk16_fpga/ice40hx8k-evb.pcf10
-rw-r--r--atk16_fpga/isa_idea.md193
-rw-r--r--atk16_fpga/mmodel_sram.v (renamed from atk16_fpga/sram.v)7
-rw-r--r--atk16_fpga/mmodel_sram_tb.v (renamed from atk16_fpga/sram_tb.v)16
-rw-r--r--atk16_fpga/sram_tb.gtkw56
-rw-r--r--atk16_fpga/sram_testing.v89
-rw-r--r--atk16_fpga/top.v175
23 files changed, 390 insertions, 1949 deletions
diff --git a/atk16_fpga/.gitignore b/atk16_fpga/.gitignore
index c09389c..bba1e09 100644
--- a/atk16_fpga/.gitignore
+++ b/atk16_fpga/.gitignore
@@ -1,9 +1,6 @@
-*.dblite
+*.bin
+*.asc
+*.json
*.out
*.vcd
-*.rpt
-*.svg
-*.dot
-hardware.bin
-hardware.json
-hardware.asc
+*.dblite
diff --git a/atk16_fpga/Makefile b/atk16_fpga/Makefile
new file mode 100644
index 0000000..cada068
--- /dev/null
+++ b/atk16_fpga/Makefile
@@ -0,0 +1,24 @@
+BIN ?= program.bin
+
+hardware.bin: top.v
+ apio verify
+ apio test
+ apio build
+
+.PHONY: hw
+hw: hardware.bin
+ apio upload
+
+.PHONY: sw
+sw: $(BIN)
+ iceprogduino -w -o 1M $(BIN)
+
+.PHONY: graph
+graph:
+ yosys -f verilog -p "show -format dot -colors 1 -prefix hardware top" -q top.v
+ dot -Tsvg hardware.dot -o hardware.svg
+ open -a LibreWolf hardware.svg
+
+.PHONY: clean
+clean:
+ apio clean
diff --git a/atk16_fpga/alu.v b/atk16_fpga/alu.v
deleted file mode 100644
index ea78328..0000000
--- a/atk16_fpga/alu.v
+++ /dev/null
@@ -1,35 +0,0 @@
-`default_nettype none
-
-module alu (
- input wire [2:0] sel,
- input wire [15:0] a,
- input wire [15:0] b,
- output reg [15:0] result,
- output reg [3:0] flags // { carry, overflow, negative, zero }
-);
- always @(*) begin
- flags[2] = 0;
- flags[3] = 0;
-
- case (sel)
- 3'd0: begin
- {flags[3], result} = a + b; // compute carry and result
- flags[2] = (a[15] == b[15] && result[15] != a[15]); // compute overflow
- end
- 3'd1: begin
- {flags[3], result} = a - b; // compute carry and result
- flags[2] = (a[15] != b[15] && result[15] != a[15]); // compute overflow
- end
- 3'd2: result = a & b;
- 3'd3: result = a | b;
- 3'd4: result = a ^ b;
- 3'd5: result = a << b;
- 3'd6: result = a >> b;
- 3'd7: result = $signed(a) >>> b;
- endcase
-
- flags[1] = ($signed(result) < 0);
- flags[0] = (result == 0);
- end
-
-endmodule
diff --git a/atk16_fpga/alu_gtkw_translate_sel.txt b/atk16_fpga/alu_gtkw_translate_sel.txt
deleted file mode 100644
index ac71a28..0000000
--- a/atk16_fpga/alu_gtkw_translate_sel.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-0 ADD
-1 MINUS
-2 AND
-3 OR
-4 XOR
-5 SHL
-6 SHR
-7 SAR
diff --git a/atk16_fpga/alu_tb.gtkw b/atk16_fpga/alu_tb.gtkw
deleted file mode 100644
index 0ef8322..0000000
--- a/atk16_fpga/alu_tb.gtkw
+++ /dev/null
@@ -1,54 +0,0 @@
-[*]
-[*] GTKWave Analyzer v3.4.0 (w)1999-2022 BSI
-[*] Tue Dec 24 12:08:19 2024
-[*]
-[dumpfile] "/Users/jantuomi/Projects/Personal/ATK16/atk16_fpga/alu_tb.vcd"
-[dumpfile_mtime] "Tue Dec 24 11:56:07 2024"
-[dumpfile_size] 1623
-[savefile] "/Users/jantuomi/Projects/Personal/ATK16/atk16_fpga/alu_tb.gtkw"
-[timestart] 0
-[size] 1280 600
-[pos] -1 -1
-*-17.259954 120300 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-[markername] AA
-[markername] BB
-[markername] CC
-[markername] DD
-[markername] EE
-[markername] FF
-[markername] GG
-[markername] HH
-[markername] II
-[markername] JJ
-[markername] KK
-[markername] LL
-[markername] MM
-[markername] NN
-[markername] OO
-[markername] PP
-[markername] QQ
-[markername] RR
-[markername] SS
-[markername] TT
-[markername] UU
-[markername] VV
-[markername] WW
-[markername] XX
-[markername] YY
-[markername] ZZ
-[treeopen] alu_tb.
-[sst_width] 240
-[signals_width] 170
-[sst_expanded] 1
-[sst_vpaned_height] 159
-@2025
-^1 /Users/jantuomi/Projects/Personal/ATK16/atk16_fpga/alu_gtkw_translate_sel.txt
-alu_tb.dut.sel[2:0]
-@420
-alu_tb.dut.a[15:0]
-alu_tb.dut.b[15:0]
-alu_tb.dut.result[15:0]
-@28
-alu_tb.dut.flags[3:0]
-[pattern_trace] 1
-[pattern_trace] 0
diff --git a/atk16_fpga/alu_tb.v b/atk16_fpga/alu_tb.v
deleted file mode 100644
index 559e23b..0000000
--- a/atk16_fpga/alu_tb.v
+++ /dev/null
@@ -1,73 +0,0 @@
-`default_nettype none
-`define DUMPSTR(x) `"x.vcd`"
-
-`define PLUS 3'd0
-`define MINUS 3'd1
-`define AND 3'd2
-`define OR 3'd3
-`define XOR 3'd4
-`define SHL 3'd5
-`define SHR 3'd6
-`define SAR 3'd7
-
-`define TESTCASE(ma, msel, mb, mexpected, mflags) \
- sel = `msel; \
- a = ma; \
- b = mb; \
- #5 if (result != $unsigned(mexpected) || flags !== mflags) begin \
- failed = 1; \
- $write("\033[0;31m[FAIL]\033[0m "); \
- $display("%d msel %0d = %0d, flags %04b", a, b, result, flags); \
- $display("_ expected result %0d", $unsigned(mexpected)); \
- $display("_ expected flags %04b", mflags); \
- end \
- else begin \
- $write("\033[0;32m[PASS]\033[0m "); \
- $display("%d msel %0d = %0d, flags %04b", a, b, result, flags); \
- end
-
-`timescale 10 ns / 100 ps
-module alu_tb();
-
- reg [2:0] sel;
- reg [15:0] a, b;
- wire [15:0] result;
- wire [3:0] flags;
-
- alu dut (
- .sel(sel),
- .a(a),
- .b(b),
- .result(result),
- .flags(flags)
- );
-
- reg failed;
- initial begin
- failed = 0;
- $dumpfile(`DUMPSTR(`VCD_OUTPUT));
- $dumpvars(0, alu_tb);
-
- `TESTCASE(16'd10, PLUS, 16'd20, 16'd30, 4'b0000)
- `TESTCASE(-16'd1, PLUS, 16'd1, 16'd0, 4'b1001)
- `TESTCASE(16'h7fff, PLUS, 16'h7fff, 16'hfffe, 4'b0110)
- `TESTCASE(16'd30, MINUS, 16'd30, 16'd0, 4'b0001)
- `TESTCASE(16'd30, MINUS, 16'd40, -16'd10, 4'b1010)
- `TESTCASE(16'h7fff, MINUS, 16'h7fff, 16'd0, 4'b0001)
- `TESTCASE(16'h8000, MINUS, 16'h1, 16'h7fff, 4'b0100)
- `TESTCASE(16'b110, AND, 16'b101, 16'b100, 4'b0000)
- `TESTCASE(16'b110, OR, 16'b101, 16'b111, 4'b0000)
- `TESTCASE(16'b110, XOR, 16'b101, 16'b011, 4'b0000)
- `TESTCASE(16'b110, SHL, 16'd3, 16'b110000, 4'b0000)
- `TESTCASE(16'b1100_0000_0000_0000, SHR, 16'd3, 16'b0001_1000_0000_0000, 4'b0000)
- `TESTCASE(16'b1100_0000_0000_0000, SAR, 16'd3, 16'b1111_1000_0000_0000, 4'b0010)
-
- #5
- if (failed) begin
- $display("🛑 \033[0;31mTest suite failed\033[0m");
- $fatal(1);
- end else
- $display("✅ \033[0;32mTest suite passed\033[0m");
- $finish;
- end
-endmodule
diff --git a/atk16_fpga/bram.v b/atk16_fpga/bram.v
deleted file mode 100644
index c814c67..0000000
--- a/atk16_fpga/bram.v
+++ /dev/null
@@ -1,37 +0,0 @@
-`default_nettype none
-
-/*
- * Copyright 2020 Brian O'Dell
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-module bram (
- input clk,
- input [15:0] addr,
- input cs_n,
- input wr_n,
- input rd_n,
- input [15:0] bram_data_in,
- output reg [15:0] bram_data_out
-);
-
- reg [15:0] mem [16'hEFFF:0];
-
- always @(posedge clk)
- if (cs_n == 1'b0) begin
- if (wr_n == 1'b0) mem[addr] <= bram_data_in;
- if (rd_n == 1'b0) bram_data_out <= mem[addr];
- end
- endmodule
diff --git a/atk16_fpga/bram_fsm.v b/atk16_fpga/bram_fsm.v
deleted file mode 100644
index 763b853..0000000
--- a/atk16_fpga/bram_fsm.v
+++ /dev/null
@@ -1,58 +0,0 @@
-`default_nettype none
-
-`define ST_IDLE 3'd0
-`define ST_READ 3'd1
-`define ST_WRITE 3'd2
-
-module bram_fsm(
- input clk,
- input [15:0] addr,
- input [15:0] data_in,
- input read_en,
- input write_en,
-
- output wire [15:0] data_out,
- output reg done
-);
- reg wr, rd;
- reg [15:0] bram_addr;
- reg [2:0] state = 0;
- bram bram_inst(
- .clk(clk),
- .addr(bram_addr),
- .cs_n(1'd0),
- .wr_n(~wr),
- .rd_n(~rd),
- .bram_data_in(data_in),
- .bram_data_out(data_out)
- );
-
- always @(posedge clk) begin
- case (state)
- `ST_IDLE: begin
- done <= 0;
- if (~done && read_en) begin
- state <= `ST_READ;
- bram_addr <= addr;
- rd <= 1;
- wr <= 0;
- end
- else if (~done && write_en) begin
- state <= `ST_WRITE;
- bram_addr <= addr;
- rd <= 0;
- wr <= 1;
- end
- end
- `ST_READ: begin
- state <= `ST_IDLE;
- done <= 1;
- end
- `ST_WRITE: begin
- state <= `ST_IDLE;
- done <= 1;
- end
- endcase
- end
-
-endmodule
diff --git a/atk16_fpga/bram_fsm_tb.gtkw b/atk16_fpga/bram_fsm_tb.gtkw
deleted file mode 100644
index 0f8b544..0000000
--- a/atk16_fpga/bram_fsm_tb.gtkw
+++ /dev/null
@@ -1,32 +0,0 @@
-[*]
-[*] GTKWave Analyzer v3.4.0 (w)1999-2022 BSI
-[*] Thu Dec 26 11:58:12 2024
-[*]
-[dumpfile] "/Users/jantuomi/Projects/Personal/ATK16/atk16_fpga/bram_fsm_tb.vcd"
-[dumpfile_mtime] "Thu Dec 26 11:57:15 2024"
-[dumpfile_size] 1302
-[savefile] "/Users/jantuomi/Projects/Personal/ATK16/atk16_fpga/bram_fsm_tb.gtkw"
-[timestart] 0
-[size] 1280 600
-[pos] -1 -1
-*-16.373917 -1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-[treeopen] bram_fsm_tb.
-[treeopen] bram_fsm_tb.dut.
-[sst_width] 240
-[signals_width] 144
-[sst_expanded] 1
-[sst_vpaned_height] 159
-@28
-bram_fsm_tb.clk
-bram_fsm_tb.start_read
-bram_fsm_tb.start_write
-@22
-bram_fsm_tb.addr[15:0]
-bram_fsm_tb.data_in[15:0]
-bram_fsm_tb.data_out[15:0]
-@28
-bram_fsm_tb.done
-@29
-bram_fsm_tb.dut.state[2:0]
-[pattern_trace] 1
-[pattern_trace] 0
diff --git a/atk16_fpga/bram_fsm_tb.v b/atk16_fpga/bram_fsm_tb.v
deleted file mode 100644
index 1ec5a88..0000000
--- a/atk16_fpga/bram_fsm_tb.v
+++ /dev/null
@@ -1,84 +0,0 @@
-`default_nettype none
-`define DUMPSTR(x) `"x.vcd`"
-`timescale 10 ns / 100 ps
-
-module bram_fsm_tb();
-
- reg clk, rst, read_en, write_en;
- reg [15:0] addr, data_in;
- wire done;
- wire [15:0] data_out;
-
- bram_fsm dut(
- .clk(clk),
- .addr(addr),
- .data_in(data_in),
- .read_en(read_en),
- .write_en(write_en),
- .data_out(data_out),
- .done(done)
- );
-
- // Clock generation
- always #5 clk = ~clk; // 100 MHz clock
-
- reg failed;
- initial begin
- failed = 0;
- $dumpfile(`DUMPSTR(`VCD_OUTPUT));
- $dumpvars(0, bram_fsm_tb);
-
- clk = 0;
- rst = 0;
- read_en = 0;
- write_en = 0;
- #10
-
- // test read
- dut.bram_inst.mem[0] = 16'hffff;
- addr = 16'd0;
- read_en = 1;
- write_en = 0;
-
- while (!done) #10;
-
- if (data_out == 16'hffff && done == 1) begin
- $display("\033[0;32m[PASS]\033[0m Read: data is available and done is 1 as expected");
- end else begin
- $display("\033[0;31m[FAIL]\033[0m Read: done is %d (expected 1), data is %04h (expected ffff)", done, data_out);
- failed = 1;
- end
-
- read_en = 0;
- write_en = 0;
- #10
-
- // test write
- addr = 16'd1;
- data_in = 16'hffff;
- read_en = 0;
- write_en = 1;
-
- while (!done) #10;
-
- if (dut.bram_inst.mem[1] == 16'hffff && done == 1) begin
- $display("\033[0;32m[PASS]\033[0m Write: data is available and done is 1 as expected");
- end else begin
- $display("\033[0;31m[FAIL]\033[0m Write: done is %d (expected 0), data is %04h (expected FFFF)", done, data_out);
- failed = 1;
- end
-
- read_en = 0;
- write_en = 0;
-
- #5
- if (failed) begin
- $display("🛑 \033[0;31mTest suite failed\033[0m");
- $fatal(1);
- end else
- $display("✅ \033[0;32mTest suite passed\033[0m");
-
- $finish;
- end
-
-endmodule
diff --git a/atk16_fpga/cu.v b/atk16_fpga/cu.v
deleted file mode 100644
index d6fb7df..0000000
--- a/atk16_fpga/cu.v
+++ /dev/null
@@ -1,524 +0,0 @@
-`default_nettype none
-
-`define PH_RESET 3'd0
-`define PH_CHECK 3'd1
-`define PH_FETCH 3'd2
-`define PH_DECODE 3'd3
-`define PH_EXECUTE 3'd4
-
-`define FL_CARRY 2'd3
-`define FL_OVERFLOW 2'd2
-`define FL_NEGATIVE 2'd1
-`define FL_ZERO 2'd0
-
-// Legend:
-// T = target register select
-// L = left operand register select
-// R = right operand register select
-// I = immediate operand
-// S = ALU operation select
-// F = flag select
-// A = absolute (1) / relative (0) addressing
-// D = direct (1) / indirect (0) load/store
-// X = unused
-
-// ALR (arithmetic-logic, register)
-// 0000 TTTL LLRR RSSS
-`define OP_ALR 4'b0000
-// ALI (arithmetic-logic, immediate)
-// 0001 TTTL LLII ISSS
-`define OP_ALI 4'b0001
-// LDR (load by register)
-// 0010 TTTR RRXX XXAD
-`define OP_LDR 4'b0010
-// STR (store to memory)
-// 0011 XXXL LLRR RXAD
-`define OP_STR 4'b0011
-// LDI (load by immediate)
-// 0100 TTTI IIII IIAD
-`define OP_LDI 4'b0100
-// JPR (jump by register)
-// 0101 ADRR RXXX XXXX
-`define OP_JPR 4'b0101
-// JPI (jump by immediate)
-// 0110 ADII IIII IIII
-`define OP_JPI 4'b0110
-// BRR (branch by register)
-// 0111 ADFF RRRX XXXX
-`define OP_BRR 4'b0111
-// BRI (branch by immediate)
-// 1000 ADFF IIII IIII
-`define OP_BRI 4'b1000
-// LPC (load PC+1)
-// 1001 TTTX XXXX XXXX
-`define OP_LPC 4'b1001
-// RTI (return from interrupt)
-// 1010 XXXX XXXX XXXX
-`define OP_RTI 4'b1010
-`define OP_NOP1 4'b1011
-`define OP_NOP2 4'b1100
-`define OP_NOP3 4'b1101
-`define OP_NOP4 4'b1110
-// HLT (halt)
-// 1111 XXXX XXXX XXXX
-`define OP_HLT 4'b1111
-
-module cu(
- input wire clk,
- input wire rst,
- input wire [3:0] int_lines,
-
- output reg [15:0] sram_addr,
- output reg [15:0] sram_in,
- input wire [15:0] sram_out,
- output wire sram_cs_n,
- output reg sram_wr_n, sram_rd_n
-);
- reg [15:0] regbank [0:7];
- reg [15:0] decoded_tmp;
- reg [2:0] phase = `PH_RESET;
- reg [15:0] pc;
- reg [15:0] ir;
- reg halted = 0;
-
- // interrupts
- reg [15:0] int_pc;
- reg [3:0] last_int_lines;
- reg [3:0] int_tmp; // used as temporary variable
- reg int_en = 1;
-
- reg sram_waiting = 0;
- assign sram_cs_n = 1'b0;
-
- reg [2:0] alu_sel;
- reg [15:0] alu_a;
- reg [15:0] alu_b;
- wire [15:0] alu_result;
- wire [3:0] alu_flags;
- alu alu_inst(
- .sel(alu_sel),
- .a(alu_a),
- .b(alu_b),
- .result(alu_result),
- .flags(alu_flags)
- );
-
- // Named reg wires for debugging purposes
- wire [15:0] ra, rb, rc, rd, re, rf, rg, rh;
- assign ra = regbank[0];
- assign rb = regbank[1];
- assign rc = regbank[2];
- assign rd = regbank[3];
- assign re = regbank[4];
- assign rf = regbank[5];
- assign rg = regbank[6];
- assign rh = regbank[7];
-
- integer i;
- always @(posedge clk or posedge rst) begin
- last_int_lines <= int_lines;
-
- // Handle reset button
- if (rst) begin
- phase <= `PH_RESET;
- halted <= 0;
- // this line is replicated in PH_RESET handling to appease
- // yosys async reset analysis
- last_int_lines <= 4'b0000;
- end
-
- else if (halted) begin
- // Do nothing
- end
-
- else if (phase == `PH_RESET) begin
- phase <= `PH_CHECK;
-
- pc <= 16'd0;
- sram_rd_n <= 1;
- sram_wr_n <= 1;
- last_int_lines <= 4'b0000;
-
- for (i = 0; i < 8; i = i + 1) begin
- regbank[i] <= 16'd0;
- end
- end
-
- // Check stage (check interrupts)
- // Compute rising edges of interrupt lines (0 -> 1) (blocking)
- else if (phase == `PH_CHECK) begin
- phase <= `PH_FETCH;
-
- if (int_en && (~last_int_lines & int_lines) != 4'b00) begin
- int_en <= 0;
- int_pc <= pc;
- casez (~last_int_lines & int_lines)
- 4'bzzz1: pc <= 16'h10;
- 4'bzz1z: pc <= 16'h11;
- 4'bz1zz: pc <= 16'h12;
- 4'b1zzz: pc <= 16'h13;
- endcase
- end
- end
-
- // Fetch stage
- else if (phase == `PH_FETCH) begin
- if (~sram_waiting) begin
- sram_rd_n <= 0;
- sram_addr <= pc;
- sram_waiting <= 1;
- end else begin
- sram_rd_n <= 1;
- ir <= sram_out;
- phase <= `PH_DECODE;
- pc <= pc + 16'd1;
- sram_waiting <= 0;
- end
- end
-
- // Decode stage
- else if (phase == `PH_DECODE) begin
- case (ir[15:12])
- `OP_ALR: begin
- alu_sel <= ir[2:0]; // select ALU operation
- alu_a <= regbank[ir[8:6]]; // read operand A
- alu_b <= regbank[ir[5:3]]; // read operand B
- phase <= `PH_EXECUTE;
- end
- `OP_ALI: begin
- alu_sel <= ir[2:0]; // select ALU operation
- alu_a <= regbank[ir[8:6]]; // read operand A
- alu_b <= ir[5:3]; // read immediate operand
- phase <= `PH_EXECUTE;
- end
- `OP_LDR: begin
- // ir[1] = A = absolute (1) / relative (0) addressing
- // ir[0] = D = direct (1) / indirect (0) store
- // set target reg value to address, dereference in execute phase
- if (ir[1] == 1 && ir[0] == 1) begin // addressing mode: absolute, direct
- decoded_tmp <= regbank[ir[8:6]];
- phase <= `PH_EXECUTE;
- end
- else if (ir[1] == 0 && ir[0] == 1) begin // addressing mode: pc relative, direct
- decoded_tmp <= pc + regbank[ir[8:6]] - 1;
- phase <= `PH_EXECUTE;
- end
- else if (ir[1] == 1 && ir[0] == 0) begin // addressing mode: absolute, indirect
- if (~sram_waiting) begin
- sram_rd_n <= 0;
- sram_addr <= regbank[ir[8:6]];
- sram_waiting <= 1;
- end else begin
- decoded_tmp <= sram_out;
- sram_rd_n <= 1;
- phase <= `PH_EXECUTE;
- sram_waiting <= 0;
- end
- end
- else if (ir[1] == 0 && ir[0] == 0) begin // addressing mode: pc relative, indirect
- if (~sram_waiting) begin
- sram_rd_n <= 0;
- sram_addr <= pc + regbank[ir[8:6]] - 1;
- sram_waiting <= 1;
- end else begin
- decoded_tmp <= sram_out;
- sram_rd_n <= 1;
- phase <= `PH_EXECUTE;
- sram_waiting <= 0;
- end
- end
- end
- `OP_STR: begin
- // ir[1] = A = absolute (1) / relative (0) addressing
- // ir[0] = D = direct (1) / indirect (0) store
- // ir[5:3] = data reg
- // ir[8:6] = address reg
- // decoded_tmp will be the address to write to
- if (ir[1] == 1 && ir[0] == 1) begin // addressing mode: absolute, direct
- decoded_tmp <= regbank[ir[8:6]];
- phase <= `PH_EXECUTE;
- end
- else if (ir[1] == 0 && ir[0] == 1) begin // addressing mode: pc relative, direct
- decoded_tmp <= pc + regbank[ir[8:6]] - 1;
- phase <= `PH_EXECUTE;
- end
- else if (ir[1] == 1 && ir[0] == 0) begin // addressing mode: absolute, indirect
- if (~sram_waiting) begin
- sram_rd_n <= 0;
- sram_addr <= regbank[ir[8:6]];
- sram_waiting <= 1;
- end else begin
- decoded_tmp <= sram_out;
- sram_rd_n <= 1;
- phase <= `PH_EXECUTE;
- sram_waiting <= 0;
- end
- end
- else if (ir[1] == 0 && ir[0] == 0) begin // addressing mode: pc relative, indirect
- if (~sram_waiting) begin
- sram_rd_n <= 0;
- sram_addr <= pc + regbank[ir[8:6]] - 1;
- sram_waiting <= 1;
- end else begin
- decoded_tmp <= sram_out;
- sram_rd_n <= 1;
- phase <= `PH_EXECUTE;
- sram_waiting <= 0;
- end
- end
- end
- `OP_LDI: begin
- // ir[1] = A = absolute (1) / relative (0) addressing
- // ir[0] = D = direct (1) / indirect (0) load
- // ir[8:2] = immediate
- // ir[11:9] = target reg
- if (ir[1] == 1) begin // addressing mode: absolute
- decoded_tmp <= ir[8:2];
- end
- else if (ir[1] == 0) begin // addressing mode: pc relative
- decoded_tmp <= pc + {{9{ir[8]}}, ir[8:2]} - 1;
- end
- phase <= `PH_EXECUTE;
- end
- `OP_JPR: begin
- // ir[11] = A = absolute (1) / relative (0) addressing
- // ir[10] = D = direct (1) / indirect (0) load
- // ir[9:6] = address reg
- if (ir[11] == 1 && ir[10] == 1) begin // addressing mode: absolute, direct
- pc <= regbank[ir[9:6]];
- phase <= `PH_CHECK;
- end
- else if (ir[11] == 0 && ir[10] == 1) begin // addressing mode: pc relative, direct
- pc <= pc + regbank[ir[9:6]] - 1;
- phase <= `PH_CHECK;
- end
- else if (ir[11] == 1 && ir[10] == 0) begin // addressing mode: absolute, indirect
- if (~sram_waiting) begin
- sram_rd_n <= 0;
- sram_addr <= regbank[ir[9:6]];
- sram_waiting <= 1;
- end else begin
- pc <= sram_out;
- sram_rd_n <= 1;
- phase <= `PH_CHECK;
- sram_waiting <= 0;
- end
- end
- else if (ir[11] == 0 && ir[10] == 0) begin // addressing mode: pc relative, indirect
- if (~sram_waiting) begin
- sram_rd_n <= 0;
- sram_addr <= pc + regbank[ir[9:6]] - 1;
- sram_waiting <= 1;
- end else begin
- pc <= sram_out;
- sram_rd_n <= 1;
- phase <= `PH_CHECK;
- sram_waiting <= 0;
- end
- end
- end
- `OP_JPI: begin
- // ir[11] = A = absolute (1) / relative (0) addressing
- // ir[10] = D = direct (1) / indirect (0) load
- // ir[9:0] = immediate
- if (ir[11] == 1 && ir[10] == 1) begin // addressing mode: absolute, direct
- pc <= {6'd0, ir[9:0]};
- phase <= `PH_CHECK;
- end
- else if (ir[11] == 0 && ir[10] == 1) begin // addressing mode: pc relative, direct
- pc <= pc + {{6{ir[9]}}, ir[9:0]} - 1;
- phase <= `PH_CHECK;
- end
- else if (ir[11] == 1 && ir[10] == 0) begin // addressing mode: absolute, indirect
- if (~sram_waiting) begin
- sram_rd_n <= 0;
- sram_addr <= {6'd0, ir[9:0]};
- sram_waiting <= 1;
- end else begin
- pc <= sram_out;
- sram_rd_n <= 1;
- phase <= `PH_CHECK;
- sram_waiting <= 0;
- end
- end
- else if (ir[11] == 0 && ir[10] == 0) begin // addressing mode: pc relative, indirect
- if (~sram_waiting) begin
- sram_rd_n <= 0;
- sram_addr <= pc + {{6{ir[9]}}, ir[9:0]} - 1;
- sram_waiting <= 1;
- end else begin
- pc <= sram_out;
- sram_rd_n <= 1;
- phase <= `PH_CHECK;
- sram_waiting <= 0;
- end
- end
- end
- `OP_BRR: begin
- // ir[11] = A = absolute (1) / relative (0) addressing
- // ir[10] = direct (1) / indirect (0) branch
- // ir[9:8] = flag select
- // ir[7:5] = address reg
- if (ir[11] == 1 && ir[10] == 1) begin // addressing mode: absolute, direct
- decoded_tmp <= regbank[ir[7:5]];
- phase <= `PH_EXECUTE;
- end
- else if (ir[11] == 0 && ir[10] == 1) begin // addressing mode: pc relative, direct
- decoded_tmp <= pc + regbank[ir[7:5]] - 1;
- phase <= `PH_EXECUTE;
- end
- else if (ir[11] == 1 && ir[10] == 0) begin // addressing mode: absolute, indirect
- if (~sram_waiting) begin
- sram_rd_n <= 0;
- sram_addr <= regbank[ir[7:5]];
- sram_waiting <= 1;
- end else begin
- decoded_tmp <= sram_out;
- sram_rd_n <= 1;
- phase <= `PH_EXECUTE;
- sram_waiting <= 0;
- end
- end
- else if (ir[11] == 0 && ir[10] == 0) begin // addressing mode: pc relative, indirect
- if (~sram_waiting) begin
- sram_rd_n <= 0;
- sram_addr <= pc + regbank[ir[7:5]] - 1;
- sram_waiting <= 1;
- end else begin
- decoded_tmp <= sram_out;
- sram_rd_n <= 1;
- phase <= `PH_EXECUTE;
- sram_waiting <= 0;
- end
- end
- end
- `OP_BRI: begin
- // ir[11] = A = absolute (1) / relative (0) addressing
- // ir[10] = direct (1) / indirect (0) branch
- // ir[9:8] = flag select
- // ir[7:0] = immediate
- if (ir[11] == 1 && ir[10] == 1) begin // addressing mode: absolute, direct
- decoded_tmp <= {8'd0, ir[7:0]};
- phase <= `PH_EXECUTE;
- end
- else if (ir[11] == 0 && ir[10] == 1) begin // addressing mode: pc relative, direct
- decoded_tmp <= pc + {{8{ir[7]}}, ir[7:0]} - 1;
- phase <= `PH_EXECUTE;
- end
- else if (ir[11] == 1 && ir[10] == 0) begin // addressing mode: absolute, indirect
- if (~sram_waiting) begin
- sram_rd_n <= 0;
- sram_addr <= {8'd0, ir[7:0]};
- sram_waiting <= 1;
- end else begin
- decoded_tmp <= sram_out;
- sram_rd_n <= 1;
- phase <= `PH_EXECUTE;
- sram_waiting <= 0;
- end
- end
- else if (ir[11] == 0 && ir[10] == 0) begin // addressing mode: pc relative, indirect
- if (~sram_waiting) begin
- sram_rd_n <= 0;
- sram_addr <= pc + {{8{ir[7]}}, ir[7:0]} - 1;
- sram_waiting <= 1;
- end else begin
- decoded_tmp <= sram_out;
- sram_rd_n <= 1;
- phase <= `PH_EXECUTE;
- sram_waiting <= 0;
- end
- end
- end
- `OP_LPC: begin
- // ir[11:9] = target reg
- regbank[ir[11:9]] <= pc;
- phase <= `PH_CHECK;
- end
- `OP_RTI: begin
- pc <= int_pc;
- int_en <= 1;
- phase <= `PH_CHECK;
- end
- `OP_HLT: begin
- halted <= 1;
- end
- default: begin
- `ifdef __SYNTHESIS__
- $fatal(1, "decode: unhandled opcode %04b", ir[15:12]);
- `endif
- end
- endcase
- end
-
- // Execute stage
- else if (phase == `PH_EXECUTE) begin
- case (ir[15:12])
- `OP_ALR, `OP_ALI: begin
- regbank[ir[11:9]] <= alu_result; // write result
- phase <= `PH_CHECK;
- end
- `OP_LDR: begin
- if (~sram_waiting) begin
- sram_rd_n <= 0;
- sram_addr <= decoded_tmp;
- sram_waiting <= 1;
- end else begin
- regbank[ir[11:9]] <= sram_out; // dereference address
- sram_rd_n <= 1;
- phase <= `PH_CHECK;
- sram_waiting <= 0;
- end
- end
- `OP_STR: begin
- if (~sram_waiting) begin
- sram_wr_n <= 0;
- sram_addr <= decoded_tmp;
- sram_in <= regbank[ir[5:3]];
- sram_waiting <= 1;
- end else begin
- sram_wr_n <= 1;
- phase <= `PH_CHECK;
- sram_waiting <= 0;
- end
- end
- `OP_LDI: begin
- // ir[1] = A = absolute (1) / relative (0) addressing
- // ir[0] = D = direct (1) / indirect (0) load
- // ir[8:2] = immediate
- // ir[11:9] = target reg
- // decoded_tmp = immediate value that takes absolute/relative to account
- if (ir[0] == 1) begin // direct
- regbank[ir[11:9]] <= decoded_tmp;
- phase <= `PH_CHECK;
- end
- else if (ir[0] == 0) begin // indirect
- if (~sram_waiting) begin
- sram_rd_n <= 0;
- sram_addr <= decoded_tmp;
- sram_waiting <= 1;
- end else begin
- regbank[ir[11:9]] <= sram_out;
- sram_rd_n <= 1;
- phase <= `PH_CHECK;
- sram_waiting <= 0;
- end
- end
- end
- `OP_BRR, `OP_BRI: begin
- // ir[9:8] = flag select
- // branch if selected flag (0..3) is set (i.e. when anded with flags is non-zero)
- if ((1 << ir[9:8]) & alu_flags != 4'd0) begin
- pc <= decoded_tmp;
- end
- phase <= `PH_CHECK;
- end
- default: begin
- `ifdef __SYNTHESIS__
- $fatal(1, "execute: unhandled opcode %04b", ir[15:12]);
- `endif
- end
- endcase
- end
- end
-endmodule
diff --git a/atk16_fpga/cu_tb.gtkw b/atk16_fpga/cu_tb.gtkw
deleted file mode 100644
index 57ec3d8..0000000
--- a/atk16_fpga/cu_tb.gtkw
+++ /dev/null
@@ -1,73 +0,0 @@
-[*]
-[*] GTKWave Analyzer v3.4.0 (w)1999-2022 BSI
-[*] Tue Dec 31 07:36:06 2024
-[*]
-[dumpfile] "/Users/jantuomi/Projects/Personal/ATK16/atk16_fpga/cu_tb.vcd"
-[dumpfile_mtime] "Tue Dec 31 07:35:29 2024"
-[dumpfile_size] 38114
-[savefile] "/Users/jantuomi/Projects/Personal/ATK16/atk16_fpga/cu_tb.gtkw"
-[timestart] 0
-[size] 1728 1051
-[pos] -1 -1
-*-18.634130 1225000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-[markername] AA
-[markername] BB
-[markername] CC
-[markername] DD
-[markername] EE
-[markername] FF
-[markername] GG
-[markername] HH
-[markername] II
-[markername] JJ
-[markername] KK
-[markername] LL
-[markername] MM
-[markername] NN
-[markername] OO
-[markername] PP
-[markername] QQ
-[markername] RR
-[markername] SS
-[markername] TT
-[markername] UU
-[markername] VV
-[markername] WW
-[markername] XX
-[markername] YY
-[markername] ZZ
-[treeopen] cu_tb.
-[treeopen] cu_tb.dut.
-[sst_width] 240
-[signals_width] 325
-[sst_expanded] 1
-[sst_vpaned_height] 317
-@25
-cu_tb.test_num[31:0]
-@28
-cu_tb.dut.clk
-cu_tb.dut.rst
-@24
-cu_tb.dut.phase[2:0]
-@22
-cu_tb.dut.pc[15:0]
-@28
-cu_tb.dut.ir[15:0]
-@420
-cu_tb.dut.alu_a[15:0]
-cu_tb.dut.alu_b[15:0]
-cu_tb.dut.alu_sel[2:0]
-cu_tb.dut.alu_result[15:0]
-@28
-cu_tb.dut.alu_flags[3:0]
-@22
-cu_tb.dut.ra[15:0]
-cu_tb.dut.rb[15:0]
-cu_tb.dut.rc[15:0]
-cu_tb.dut.rd[15:0]
-cu_tb.dut.re[15:0]
-cu_tb.dut.rf[15:0]
-cu_tb.dut.rg[15:0]
-cu_tb.dut.rh[15:0]
-[pattern_trace] 1
-[pattern_trace] 0
diff --git a/atk16_fpga/cu_tb.v b/atk16_fpga/cu_tb.v
deleted file mode 100644
index d472ac3..0000000
--- a/atk16_fpga/cu_tb.v
+++ /dev/null
@@ -1,587 +0,0 @@
-`default_nettype none
-`define DUMPSTR(x) `"x.vcd`"
-`timescale 1 ns / 100 ps
-
-module cu_tb();
-
- reg clk, rst;
- reg [3:0] int_lines;
-
- wire sram_cs_n, sram_wr_n, sram_rd_n;
- wire [17:0] sram_addr;
- wire [15:0] sram_data_in;
- wire [15:0] sram_data_out;
- sram sram_inst(
- .cs_n(sram_cs_n),
- .wr_n(sram_wr_n),
- .rd_n(sram_rd_n),
- .addr(sram_addr),
- .data_in(sram_data_in),
- .data_out(sram_data_out)
- );
-
- wire [15:0] sram_addr_short;
- assign sram_addr = { 2'b0, sram_addr_short };
-
- cu dut(
- .clk(clk),
- .rst(rst),
- .int_lines(int_lines),
-
- .sram_cs_n(sram_cs_n),
- .sram_wr_n(sram_wr_n),
- .sram_rd_n(sram_rd_n),
- .sram_addr(sram_addr_short),
- .sram_in(sram_data_in),
- .sram_out(sram_data_out)
- );
-
- // Clock generation
- always #5 clk = ~clk; // 100 MHz clock
-
- reg failed;
- reg [15:0] expected, expected2;
- reg [31:0] test_num = 0;
- initial begin
- failed = 0;
- $dumpfile(`DUMPSTR(`VCD_OUTPUT));
- $dumpvars(0, cu_tb);
-
- clk = 0;
- rst = 0;
- int_lines = 4'b0000;
-
- #10 // Wait for reset phase to finish
-
- // set up halt instruction at PC = 1, used by all tests
- sram_inst.mem[1] = { 4'b1111, 12'd0 }; // HLT
-
- // test ALR
- dut.regbank[0] = 16'd10;
- dut.regbank[1] = 16'd20;
- sram_inst.mem[0] = { 4'b0000, 3'd2, 3'd0, 3'd1, 3'd0 }; // ALR RC, RA, RB, S=PLUS
- expected = 16'd30;
-
- #200 if (dut.regbank[2] == expected) begin
- $display("\033[0;32m[PASS]\033[0m [%0d] ALR ok", test_num);
- end else begin
- $display("\033[0;31m[FAIL]\033[0m [%0d] ALR not working, target reg contains %04h, expected %04h", test_num, dut.regbank[2], expected);
- failed = 1;
- end
- rst = 1; #10 rst = 0; #10 test_num++;
-
- // test ALI
- dut.regbank[0] = 16'd10;
- sram_inst.mem[0] = { 4'b0001, 3'd2, 3'd0, 3'd7, 3'd0 }; // ALI RC, RA, 7, S=PLUS
- expected = 16'd17;
-
- #200 if (dut.regbank[2] == expected) begin
- $display("\033[0;32m[PASS]\033[0m [%0d] ALI ok", test_num);
- end else begin
- $display("\033[0;31m[FAIL]\033[0m [%0d] ALI not working, target reg contains %04h, expected %04h", test_num, dut.regbank[2], expected);
- failed = 1;
- end
- rst = 1; #10 rst = 0; #10 test_num++;
-
- // test LDR, absolute addressing
- sram_inst.mem[16'd100] = 16'hffff;
- dut.regbank[1] = 16'd100;
- sram_inst.mem[16'd0] = { 4'b0010, 3'd0, 3'd1, 4'd0, 1'd1, 1'd1 }; // LDR RA, RB, absolute, direct
- expected = 16'hffff;
-
- #200 if (dut.regbank[0] == expected) begin
- $display("\033[0;32m[PASS]\033[0m [%0d] LDR direct absolute ok", test_num);
- end else begin
- $display("\033[0;31m[FAIL]\033[0m [%0d] LDR direct absolute not working, target reg contains %04h, expected %04h", test_num, dut.regbank[0], expected);
- failed = 1;
- end
- rst = 1; #10 rst = 0; #10 test_num++;
-
- // test LDR, relative addressing
- sram_inst.mem[16'd10] = 16'hffff;
- dut.regbank[1] = 16'd10;
- sram_inst.mem[16'd0] = { 4'b0010, 3'd0, 3'd1, 4'd0, 1'd0, 1'd1 }; // LDR RA, RB, relative, direct
- expected = 16'hffff;
-
- #200 if (dut.regbank[0] == expected) begin
- $display("\033[0;32m[PASS]\033[0m [%0d] LDR direct relative ok", test_num);
- end else begin
- $display("\033[0;31m[FAIL]\033[0m [%0d] LDR direct relative not working, target reg contains %04h, expected %04h", test_num, dut.regbank[0], expected);
- failed = 1;
- end
- rst = 1; #10 rst = 0; #10 test_num++;
-
- // test LDR, absolute addressing, indirect
- sram_inst.mem[16'd100] = 16'd200;
- sram_inst.mem[16'd200] = 16'hffff;
- dut.regbank[1] = 16'd100;
- sram_inst.mem[16'd0] = { 4'b0010, 3'd0, 3'd1, 4'd0, 1'd1, 1'd0 }; // LDR RA, RB, absolute, indirect
- expected = 16'hffff;
-
- #200 if (dut.regbank[0] == expected) begin
- $display("\033[0;32m[PASS]\033[0m [%0d] LDR indirect absolute ok", test_num);
- end else begin
- $display("\033[0;31m[FAIL]\033[0m [%0d] LDR indirect absolute not working, target reg contains %04h, expected %04h", test_num, dut.regbank[0], expected);
- failed = 1;
- end
- rst = 1; #10 rst = 0; #10 test_num++;
-
- // test LDR, relative addressing, indirect
- sram_inst.mem[16'd10] = 16'd20;
- sram_inst.mem[16'd20] = 16'hffff;
- dut.regbank[1] = 16'd10;
- sram_inst.mem[16'd0] = { 4'b0010, 3'd0, 3'd1, 4'd0, 1'd0, 1'd0 }; // LDR RA, RB, relative, indirect
- expected = 16'hffff;
-
- #200 if (dut.regbank[0] == expected) begin
- $display("\033[0;32m[PASS]\033[0m [%0d] LDR indirect relative ok", test_num);
- end else begin
- $display("\033[0;31m[FAIL]\033[0m [%0d] LDR indirect relative not working, target reg contains %04h, expected %04h", test_num, dut.regbank[0], expected);
- failed = 1;
- end
- rst = 1; #10 rst = 0; #10 test_num++;
-
- // test STR, absolute addressing, direct
- dut.regbank[0] = 16'hfafa;
- dut.regbank[1] = 16'd100;
- sram_inst.mem[16'd0] = { 4'b0011, 3'd0, 3'd1, 4'd0, 1'd1, 1'd1 }; // STR RB, RA, absolute, direct
- expected = 16'hfafa;
-
- #200 if (sram_inst.mem[16'd100] == expected) begin
- $display("\033[0;32m[PASS]\033[0m [%0d] STR direct absolute ok", test_num);
- end else begin
- $display("\033[0;31m[FAIL]\033[0m [%0d] STR direct absolute not working, memory location contains %04h, expected %04h", test_num, sram_inst.mem[16'd100], expected);
- failed = 1;
- end
- rst = 1; #10 rst = 0; #10 test_num++;
-
- // test STR, relative addressing, direct
- dut.regbank[0] = 16'hafaf;
- dut.regbank[1] = 16'd10;
- sram_inst.mem[16'd0] = { 4'b0011, 3'd0, 3'd1, 4'd0, 1'd0, 1'd1 }; // STR RB, RA, relative, direct
- expected = 16'hafaf;
-
- #200 if (sram_inst.mem[16'd10] == expected) begin
- $display("\033[0;32m[PASS]\033[0m [%0d] STR direct relative ok", test_num);
- end else begin
- $display("\033[0;31m[FAIL]\033[0m [%0d] STR direct relative not working, memory location contains %04h, expected %04h", test_num, sram_inst.mem[16'd10], expected);
- failed = 1;
- end
- rst = 1; #10 rst = 0; #10 test_num++;
-
- // test STR, absolute addressing, indirect
- dut.regbank[0] = 16'hfafa;
- dut.regbank[1] = 16'd100;
- sram_inst.mem[16'd100] = 16'd200;
- sram_inst.mem[16'd0] = { 4'b0011, 3'd0, 3'd1, 4'd0, 1'd1, 1'd0 }; // STR RB, RA, absolute, indirect
- expected = 16'hfafa;
-
- #200 if (sram_inst.mem[16'd200] == expected) begin
- $display("\033[0;32m[PASS]\033[0m [%0d] STR indirect absolute ok", test_num);
- end else begin
- $display("\033[0;31m[FAIL]\033[0m [%0d] STR indirect absolute not working, memory location contains %04h, expected %04h", test_num, sram_inst.mem[16'd200], expected);
- failed = 1;
- end
- rst = 1; #10 rst = 0; #10 test_num++;
-
- // test STR, relative addressing, indirect
- dut.regbank[0] = 16'hafaf;
- dut.regbank[1] = -16'd10;
- sram_inst.mem[16'd10] = 16'd20;
- sram_inst.mem[16'd20] = { 4'b0011, 3'd0, 3'd1, 4'd0, 1'd0, 1'd0 }; // STR RB, RA, relative, indirect
- dut.pc = 16'd20;
- expected = 16'hafaf;
-
- #200 if (sram_inst.mem[16'd20] == expected) begin
- $display("\033[0;32m[PASS]\033[0m [%0d] STR indirect relative ok", test_num);
- end else begin
- $display("\033[0;31m[FAIL]\033[0m [%0d] STR indirect relative not working, memory location contains %04h, expected %04h", test_num, sram_inst.mem[16'd20], expected);
- failed = 1;
- end
- rst = 1; #10 rst = 0; #10 test_num++;
-
- // test LDI, absolute addressing, direct
- sram_inst.mem[16'd0] = { 4'b0100, 3'd0, 7'd123, 1'd1, 1'd1 }; // LDI RA, 1, absolute, direct
- expected = 16'd123;
-
- #200 if (dut.regbank[0] == expected) begin
- $display("\033[0;32m[PASS]\033[0m [%0d] LDI direct absolute ok", test_num);
- end else begin
- $display("\033[0;31m[FAIL]\033[0m [%0d] LDI direct absolute not working, target reg contains %04h, expected %04h", test_num, dut.regbank[0], expected);
- failed = 1;
- end
- rst = 1; #10 rst = 0; #10 test_num++;
-
- // test LDI, relative addressing, direct
- sram_inst.mem[16'd150] = { 4'b0100, 3'd0, -7'd20, 1'd0, 1'd1 }; // LDI RA, 1, relative, direct
- dut.pc = 16'd150;
- expected = 16'd130;
-
- #200 if (dut.regbank[0] == expected) begin
- $display("\033[0;32m[PASS]\033[0m [%0d] LDI direct relative ok", test_num);
- end else begin
- $display("\033[0;31m[FAIL]\033[0m [%0d] LDI direct relative not working, target reg contains %04h, expected %04h", test_num, dut.regbank[0], expected);
- failed = 1;
- end
- rst = 1; #10 rst = 0; #10 test_num++;
-
- // test LDI, absolute addressing, indirect
- sram_inst.mem[16'd0] = { 4'b0100, 3'd0, 7'd50, 1'd1, 1'd0 }; // LDI RA, 1, absolute, indirect
- sram_inst.mem[16'd50] = 16'd123;
- expected = 16'd123;
-
- #200 if (dut.regbank[0] == expected) begin
- $display("\033[0;32m[PASS]\033[0m [%0d] LDI indirect absolute ok", test_num);
- end else begin
- $display("\033[0;31m[FAIL]\033[0m [%0d] LDI indirect absolute not working, target reg contains %04h, expected %04h", test_num, dut.regbank[0], expected);
- failed = 1;
- end
- rst = 1; #10 rst = 0; #10 test_num++;
-
- // test LDI, relative addressing, indirect
- sram_inst.mem[16'd150] = { 4'b0100, 3'd0, -7'd20, 1'd0, 1'd0 }; // LDI RA, 1, relative, indirect
- sram_inst.mem[16'd130] = 16'd123;
- dut.pc = 16'd150;
- expected = 16'd123;
-
- #200 if (dut.regbank[0] == expected) begin
- $display("\033[0;32m[PASS]\033[0m [%0d] LDI indirect relative ok", test_num);
- end else begin
- $display("\033[0;31m[FAIL]\033[0m [%0d] LDI indirect relative not working, target reg contains %04h, expected %04h", test_num, dut.regbank[0], expected);
- failed = 1;
- end
- rst = 1; #10 rst = 0; #10 test_num++;
-
- // test JPR, absolute addressing, direct
- dut.regbank[0] = 16'd100;
- sram_inst.mem[16'd0] = { 4'b0101, 1'd1, 1'd1, 3'd0, 7'd0 }; // JPR RA, absolute, direct
- sram_inst.mem[16'd100] = { 4'b1111, 12'd0 }; // HLT
- expected = 16'd101; // PC ends up at &HLT + 1
-
- #200 if (dut.pc == expected) begin
- $display("\033[0;32m[PASS]\033[0m [%0d] JPR direct absolute ok", test_num);
- end else begin
- $display("\033[0;31m[FAIL]\033[0m [%0d] JPR direct absolute not working, PC contains %04h, expected: %04h", test_num, dut.pc, expected);
- failed = 1;
- end
- rst = 1; #10 rst = 0; #10 test_num++;
-
- // test JPR, relative addressing, direct
- dut.regbank[0] = 16'd50;
- sram_inst.mem[16'd0] = { 4'b0101, 1'd0, 1'd1, 3'd0, 7'd0 }; // JPR RA, relative, direct
- sram_inst.mem[16'd50] = { 4'b1111, 12'd0 }; // HLT
- expected = 16'd51; // PC ends up at &HLT + 1
-
- #200 if (dut.pc == expected) begin
- $display("\033[0;32m[PASS]\033[0m [%0d] JPR direct relative ok", test_num);
- end else begin
- $display("\033[0;31m[FAIL]\033[0m [%0d] JPR direct relative not working, PC contains %04h, expected: %04h", test_num, dut.pc, expected);
- failed = 1;
- end
- rst = 1; #10 rst = 0; #10 test_num++;
-
- // test JPR, absolute addressing, indirect
- dut.regbank[0] = 16'd100;
- sram_inst.mem[16'd100] = 16'd200;
- sram_inst.mem[16'd0] = { 4'b0101, 1'd1, 1'd0, 3'd0, 7'd0 }; // JPR RA, absolute, indirect
- sram_inst.mem[16'd200] = { 4'b1111, 12'd0 }; // HLT
- expected = 16'd201; // PC ends up at &HLT + 1
-
- #200 if (dut.pc == expected) begin
- $display("\033[0;32m[PASS]\033[0m [%0d] JPR indirect absolute ok", test_num);
- end else begin
- $display("\033[0;31m[FAIL]\033[0m [%0d] JPR indirect absolute not working, PC contains %04h, expected: %04h", test_num, dut.pc, expected);
- failed = 1;
- end
- rst = 1; #10 rst = 0; #10 test_num++;
-
- // test JPR, relative addressing, indirect
- dut.regbank[0] = 16'd50;
- sram_inst.mem[16'd60] = 16'd100;
- sram_inst.mem[16'd10] = { 4'b0101, 1'd0, 1'd0, 3'd0, 7'd0 }; // JPR RA, relative, indirect
- sram_inst.mem[16'd100] = { 4'b1111, 12'd0 }; // HLT
- dut.pc = 16'd10;
- expected = 16'd101; // PC ends up at &HLT + 1
-
- #200 if (dut.pc == expected) begin
- $display("\033[0;32m[PASS]\033[0m [%0d] JPR indirect relative ok", test_num);
- end else begin
- $display("\033[0;31m[FAIL]\033[0m [%0d] JPR indirect relative not working, PC contains %04h, expected: %04h", test_num, dut.pc, expected);
- failed = 1;
- end
- rst = 1; #10 rst = 0; #10 test_num++;
-
- // test JPI, absolute addressing, direct
- sram_inst.mem[16'd0] = { 4'b0110, 1'd1, 1'd1, 10'd123 }; // JPI 123, absolute, direct
- sram_inst.mem[16'd123] = { 4'b1111, 12'd0 }; // HLT
- expected = 16'd124; // PC ends up at &HLT + 1
-
- #200 if (dut.pc == expected) begin
- $display("\033[0;32m[PASS]\033[0m [%0d] JPI direct absolute ok", test_num);
- end else begin
- $display("\033[0;31m[FAIL]\033[0m [%0d] JPI direct absolute not working, PC contains %04h, expected: %04h", test_num, dut.pc, expected);
- failed = 1;
- end
- rst = 1; #10 rst = 0; #10 test_num++;
-
- // test JPI, relative addressing, direct
- sram_inst.mem[16'd30] = { 4'b0110, 1'd0, 1'd1, -10'd20 }; // JPI -20, relative, direct
- sram_inst.mem[16'd10] = { 4'b1111, 12'd0 }; // HLT
- dut.pc = 16'd30;
- expected = 16'd11; // PC ends up at &HLT + 1
-
- #200 if (dut.pc == expected) begin
- $display("\033[0;32m[PASS]\033[0m [%0d] JPI direct relative ok", test_num);
- end else begin
- $display("\033[0;31m[FAIL]\033[0m [%0d] JPI direct relative not working, PC contains %04h, expected: %04h", test_num, dut.pc, expected);
- failed = 1;
- end
- rst = 1; #10 rst = 0; #10 test_num++;
-
- // test JPI, absolute addressing, indirect
- sram_inst.mem[16'd10] = { 4'b0110, 1'd1, 1'd0, 10'd50 }; // JPI 50, absolute, indirect
- sram_inst.mem[16'd50] = 16'd123;
- sram_inst.mem[16'd123] = { 4'b1111, 12'd0 }; // HLT
- dut.pc = 16'd10;
- expected = 16'd124; // PC ends up at &HLT + 1
-
- #200 if (dut.pc == expected) begin
- $display("\033[0;32m[PASS]\033[0m [%0d] JPI indirect absolute ok", test_num);
- end else begin
- $display("\033[0;31m[FAIL]\033[0m [%0d] JPI indirect absolute not working, PC contains %04h, expected: %04h", test_num, dut.pc, expected);
- failed = 1;
- end
- rst = 1; #10 rst = 0; #10 test_num++;
-
- // test JPI, relative addressing, indirect
- sram_inst.mem[16'd30] = { 4'b0110, 1'd0, 1'd0, -10'd20 }; // JPI -20, relative, indirect
- sram_inst.mem[16'd10] = 16'd50;
- sram_inst.mem[16'd50] = { 4'b1111, 12'd0 }; // HLT
- dut.pc = 16'd30;
- expected = 16'd51; // PC ends up at &HLT + 1
-
- #200 if (dut.pc == expected) begin
- $display("\033[0;32m[PASS]\033[0m [%0d] JPI indirect relative ok", test_num);
- end else begin
- $display("\033[0;31m[FAIL]\033[0m [%0d] JPI indirect relative not working, PC contains %04h, expected: %04h", test_num, dut.pc, expected);
- failed = 1;
- end
- rst = 1; #10 rst = 0; #10 test_num++;
-
- // test BRR, absolute addressing, direct, true branch
- dut.regbank[0] = 16'd100;
- dut.regbank[2] = 16'd110;
- sram_inst.mem[16'd0] = { 4'b0000, 3'd1, 3'd0, 3'd0, 3'd1 }; // ALR RB, RA, RA, S=MINUS
- sram_inst.mem[16'd1] = { 4'b0111, 1'd1, 1'd1, 2'd0, 3'd2, 5'd0 }; // BRR RC, absolute, direct, F=ZERO
- sram_inst.mem[16'd2] = { 4'b1111, 12'd0 }; // HLT
- sram_inst.mem[16'd110] = { 4'b1111, 12'd0 }; // HLT
- expected = 16'd111; // PC ends up at &HLT + 1
-
- #200 if (dut.pc == expected) begin
- $display("\033[0;32m[PASS]\033[0m [%0d] BRR direct absolute true ok", test_num);
- end else begin
- $display("\033[0;31m[FAIL]\033[0m [%0d] BRR direct absolute true not working, PC contains %04h, expected: %04h", test_num, dut.pc, expected);
- failed = 1;
- end
- rst = 1; #10 rst = 0; #10 test_num++;
-
- // test BRR, absolute addressing, direct, false branch
- dut.regbank[0] = 16'd100;
- dut.regbank[2] = 16'd110;
- sram_inst.mem[16'd0] = { 4'b0000, 3'd1, 3'd0, 3'd0, 3'd1 }; // ALR RB, RA, RA, S=MINUS
- sram_inst.mem[16'd1] = { 4'b0111, 1'd1, 1'd1, 2'd1, 3'd2, 5'd0 }; // BRR RC, absolute, direct, F=NEGATIVE
- sram_inst.mem[16'd2] = { 4'b1111, 12'd0 }; // HLT
- sram_inst.mem[16'd110] = { 4'b1111, 12'd0 }; // HLT
- expected = 16'd3; // PC ends up at &HLT + 1
-
- #200 if (dut.pc == expected) begin
- $display("\033[0;32m[PASS]\033[0m [%0d] BRR direct absolute false ok", test_num);
- end else begin
- $display("\033[0;31m[FAIL]\033[0m [%0d] BRR direct absolute false not working, PC contains %04h, expected: %04h", test_num, dut.pc, expected);
- failed = 1;
- end
- rst = 1; #10 rst = 0; #10 test_num++;
-
- // test BRR, relative addressing, direct
- dut.regbank[0] = 16'd100;
- dut.regbank[2] = 16'd109;
- dut.pc = 16'd10;
- sram_inst.mem[16'd10] = { 4'b0000, 3'd1, 3'd0, 3'd0, 3'd1 }; // ALR RB, RA, RA, S=MINUS
- sram_inst.mem[16'd11] = { 4'b0111, 1'd0, 1'd1, 2'd0, 3'd2, 5'd0 }; // BRR RC, relative, direct, F=ZERO
- sram_inst.mem[16'd12] = { 4'b1111, 12'd0 }; // HLT
- sram_inst.mem[16'd120] = { 4'b1111, 12'd0 }; // HLT
- expected = 16'd121; // PC ends up at &HLT + 1
-
- #200 if (dut.pc == expected) begin
- $display("\033[0;32m[PASS]\033[0m [%0d] BRR direct relative ok", test_num);
- end else begin
- $display("\033[0;31m[FAIL]\033[0m [%0d] BRR direct relative not working, PC contains %04h, expected: %04h", test_num, dut.pc, expected);
- failed = 1;
- end
- rst = 1; #10 rst = 0; #10 test_num++;
-
- // test BRR, absolute addressing, indirect
- dut.regbank[0] = 16'd100;
- dut.regbank[2] = 16'd110;
- sram_inst.mem[16'd110] = 16'd200;
- sram_inst.mem[16'd0] = { 4'b0000, 3'd1, 3'd0, 3'd0, 3'd1 }; // ALR RB, RA, RA, S=MINUS
- sram_inst.mem[16'd1] = { 4'b0111, 1'd1, 1'd0, 2'd0, 3'd2, 5'd0 }; // BRR RC, absolute, indirect, F=ZERO
- sram_inst.mem[16'd2] = { 4'b1111, 12'd0 }; // HLT
- sram_inst.mem[16'd200] = { 4'b1111, 12'd0 }; // HLT
- expected = 16'd201; // PC ends up at &HLT + 1
-
- #200 if (dut.pc == expected) begin
- $display("\033[0;32m[PASS]\033[0m [%0d] BRR indirect absolute ok", test_num);
- end else begin
- $display("\033[0;31m[FAIL]\033[0m [%0d] BRR indirect absolute not working, PC contains %04h, expected: %04h", test_num, dut.pc, expected);
- failed = 1;
- end
- rst = 1; #10 rst = 0; #10 test_num++;
-
- // test BRR, relative addressing, indirect
- dut.regbank[0] = 16'd100;
- dut.regbank[2] = 16'd109;
- dut.pc = 16'd10;
- sram_inst.mem[16'd10] = { 4'b0000, 3'd1, 3'd0, 3'd0, 3'd1 }; // ALR RB, RA, RA, S=MINUS
- sram_inst.mem[16'd11] = { 4'b0111, 1'd0, 1'd0, 2'd0, 3'd2, 5'd0 }; // BRR RC, relative, indirect, F=ZERO
- sram_inst.mem[16'd12] = { 4'b1111, 12'd0 }; // HLT
- sram_inst.mem[16'd120] = 16'd130;
- sram_inst.mem[16'd130] = { 4'b1111, 12'd0 }; // HLT
- expected = 16'd131; // PC ends up at &HLT + 1
-
- #200 if (dut.pc == expected) begin
- $display("\033[0;32m[PASS]\033[0m [%0d] BRR indirect relative ok", test_num);
- end else begin
- $display("\033[0;31m[FAIL]\033[0m [%0d] BRR indirect relative not working, PC contains %04h, expected: %04h", test_num, dut.pc, expected);
- failed = 1;
- end
- rst = 1; #10 rst = 0; #10 test_num++;
-
- // test BRI, absolute addressing, direct, true branch
- dut.regbank[0] = 16'd100;
- sram_inst.mem[16'd0] = { 4'b0000, 3'd1, 3'd0, 3'd0, 3'd1 }; // ALR RB, RA, RA, S=MINUS
- sram_inst.mem[16'd1] = { 4'b1000, 1'd1, 1'd1, 2'd0, 8'd100 }; // BRI 100, absolute, direct, F=ZERO
- sram_inst.mem[16'd2] = { 4'b1111, 12'd0 }; // HLT
- sram_inst.mem[16'd100] = { 4'b1111, 12'd0 }; // HLT
- expected = 16'd101; // PC ends up at &HLT + 1
-
- #200 if (dut.pc == expected) begin
- $display("\033[0;32m[PASS]\033[0m [%0d] BRI direct absolute true ok", test_num);
- end else begin
- $display("\033[0;31m[FAIL]\033[0m [%0d] BRI direct absolute true not working, PC contains %04h, expected: %04h", test_num, dut.pc, expected);
- failed = 1;
- end
- rst = 1; #10 rst = 0; #10 test_num++;
-
- // test BRI, absolute addressing, direct, false branch
- dut.regbank[0] = 16'd100;
- sram_inst.mem[16'd0] = { 4'b0000, 3'd1, 3'd0, 3'd0, 3'd1 }; // ALR RB, RA, RA, S=MINUS
- sram_inst.mem[16'd1] = { 4'b1000, 1'd1, 1'd1, 2'd1, 8'd100 }; // BRI 100, absolute, direct, F=NEGATIVE
- sram_inst.mem[16'd2] = { 4'b1111, 12'd0 }; // HLT
- sram_inst.mem[16'd100] = { 4'b1111, 12'd0 }; // HLT
- expected = 16'd3; // PC ends up at &HLT + 1
-
- #200 if (dut.pc == expected) begin
- $display("\033[0;32m[PASS]\033[0m [%0d] BRI direct absolute false ok", test_num);
- end else begin
- $display("\033[0;31m[FAIL]\033[0m [%0d] BRI direct absolute false not working, PC contains %04h, expected: %04h", test_num, dut.pc, expected);
- failed = 1;
- end
- rst = 1; #10 rst = 0; #10 test_num++;
-
- // test BRI, relative addressing, direct
- dut.regbank[0] = 16'd100;
- dut.pc = 16'd10;
- sram_inst.mem[16'd10] = { 4'b0000, 3'd1, 3'd0, 3'd0, 3'd1 }; // ALR RB, RA, RA, S=MINUS
- sram_inst.mem[16'd11] = { 4'b1000, 1'd0, 1'd1, 2'd0, 8'd9 }; // BRI 9, relative, direct, F=ZERO
- sram_inst.mem[16'd12] = { 4'b1111, 12'd0 }; // HLT
- sram_inst.mem[16'd20] = { 4'b1111, 12'd0 }; // HLT
- expected = 16'd21; // PC ends up at &HLT + 1
-
- #200 if (dut.pc == expected) begin
- $display("\033[0;32m[PASS]\033[0m [%0d] BRI direct relative ok", test_num);
- end else begin
- $display("\033[0;31m[FAIL]\033[0m [%0d] BRI direct relative not working, PC contains %04h, expected: %04h", test_num, dut.pc, expected);
- failed = 1;
- end
- rst = 1; #10 rst = 0; #10 test_num++;
-
- // test BRI, absolute addressing, indirect
- dut.regbank[0] = 16'd100;
- sram_inst.mem[16'd100] = 16'd200;
- sram_inst.mem[16'd0] = { 4'b0000, 3'd1, 3'd0, 3'd0, 3'd1 }; // ALR RB, RA, RA, S=MINUS
- sram_inst.mem[16'd1] = { 4'b1000, 1'd1, 1'd0, 2'd0, 8'd100 }; // BRI 100, absolute, indirect, F=ZERO
- sram_inst.mem[16'd2] = { 4'b1111, 12'd0 }; // HLT
- sram_inst.mem[16'd200] = { 4'b1111, 12'd0 }; // HLT
- expected = 16'd201; // PC ends up at &HLT + 1
-
- #200 if (dut.pc == expected) begin
- $display("\033[0;32m[PASS]\033[0m [%0d] BRI indirect absolute ok", test_num);
- end else begin
- $display("\033[0;31m[FAIL]\033[0m [%0d] BRI indirect absolute not working, PC contains %04h, expected: %04h", test_num, dut.pc, expected);
- failed = 1;
- end
- rst = 1; #10 rst = 0; #10 test_num++;
-
- // test BRI, relative addressing, indirect
- dut.regbank[0] = 16'd100;
- dut.pc = 16'd10;
- sram_inst.mem[16'd10] = { 4'b0000, 3'd1, 3'd0, 3'd0, 3'd1 }; // ALR RB, RA, RA, S=MINUS
- sram_inst.mem[16'd11] = { 4'b1000, 1'd0, 1'd0, 2'd0, 8'd9 }; // BRI 9, relative, indirect, F=ZERO
- sram_inst.mem[16'd12] = { 4'b1111, 12'd0 }; // HLT
- sram_inst.mem[16'd20] = 16'd130;
- sram_inst.mem[16'd130] = { 4'b1111, 12'd0 }; // HLT
- expected = 16'd131; // PC ends up at &HLT + 1
-
- #200 if (dut.pc == expected) begin
- $display("\033[0;32m[PASS]\033[0m [%0d] BRI indirect relative ok", test_num);
- end else begin
- $display("\033[0;31m[FAIL]\033[0m [%0d] BRI indirect relative not working, PC contains %04h, expected: %04h", test_num, dut.pc, expected);
- failed = 1;
- end
- rst = 1; #10 rst = 0; #10 test_num++;
-
- // test interrupt handling
- sram_inst.mem[16'd0] = { 4'b0001, 3'b0, 3'b0, 3'b0, 3'b0 }; // NOP (add imm zero)
- sram_inst.mem[16'h10] = { 4'b1111, 12'd0 }; // HLT
- expected = 16'h11;
- expected2 = 16'd0;
- #5
- int_lines = 4'b1001; // set interrupts
-
- #200 if (dut.pc == expected && dut.int_pc == expected2) begin
- $display("\033[0;32m[PASS]\033[0m [%0d] interrupt jump ok", test_num);
- end else begin
- $display("\033[0;31m[FAIL]\033[0m [%0d] interrupt jump not working, PC contains %04h (expected: %04h), int_pc contains %04h (expected: %04h)", test_num, dut.pc, expected, dut.int_pc, expected2);
- failed = 1;
- end
- rst = 1; #10 rst = 0; #10 test_num++;
-
- // test RTI
- dut.int_en = 1'b0;
- dut.pc = 16'h10;
- dut.int_pc = 16'd1;
- sram_inst.mem[16'h10] = { 4'b1010, 12'd0 }; // RTI
- sram_inst.mem[16'd1] = { 4'b1111, 12'd0 }; // HLT
- expected = 16'd2; // PC ends up at &HLT + 1
- expected2 = 1'b1;
-
- #200 if (dut.pc == expected && dut.int_en == expected2) begin
- $display("\033[0;32m[PASS]\033[0m [%0d] interrupt return ok", test_num);
- end else begin
- $display("\033[0;31m[FAIL]\033[0m [%0d] interrupt return not working, PC contains %04h (expected: %04h), int_en contains %01b (expected: %01b)", test_num, dut.pc, expected, dut.int_en, expected2);
- failed = 1;
- end
- rst = 1; #10 rst = 0; #10 test_num++;
-
- #200
- if (failed) begin
- $display("🛑 \033[0;31mTest suite failed\033[0m");
- //$fatal(1);
- end else
- $display("✅ \033[0;32mTest suite passed\033[0m");
-
- $finish;
- end
-
-endmodule
diff --git a/atk16_fpga/flash.v b/atk16_fpga/flash.v
deleted file mode 100644
index 908ecf0..0000000
--- a/atk16_fpga/flash.v
+++ /dev/null
@@ -1,60 +0,0 @@
-`timescale 1ns / 1ps
-
-// This is a memory model of the flash memory chip on the iCE40HX8K-EVB.
-// Relevant specs:
-// - 2MB space (not implemented entirely in simulation)
-// - An SPI interface
-module flash(
- input wire cs, // Chip select, active low
- input wire clk, // SPI clock
- input wire mosi, // Master Out Slave In
- output reg miso, // Master In Slave Out
- input wire rst_n // Active low reset
-);
-
- reg [7:0] memory [0:2047]; // A smaller memory space for simplicity
- reg [7:0] command;
- reg [10:0] address;
- reg [7:0] temp_data;
- reg [3:0] bit_count;
- reg write_enable;
-
- always @(posedge clk or negedge rst_n) begin
- if (!rst_n) begin
- miso <= 1'b0;
- command <= 8'b0;
- address <= 11'b0;
- temp_data <= 8'b0;
- bit_count <= 4'b0;
- write_enable <= 1'b0;
- end else if (!cs) begin
- if (bit_count < 8) begin
- // First byte is the command
- command[7 - bit_count] <= mosi;
- end else if (bit_count < 19) begin
- // Next 11 bits are the address
- address[18 - bit_count] <= mosi;
- end else if (command == 8'h02 && write_enable) begin
- // Handle write command
- if (bit_count < 28) begin
- temp_data[27 - bit_count] <= mosi;
- if (bit_count == 27) begin
- memory[address] <= temp_data;
- end
- end
- end else if (command == 8'h03) begin
- // Handle read command
- if (bit_count == 19) begin
- temp_data <= memory[address];
- end
- if (bit_count >= 20) begin
- miso <= temp_data[27 - bit_count];
- end
- end
- bit_count <= bit_count + 1'b1;
- end else begin
- bit_count <= 0;
- miso <= 1'bz; // Tristate when not active
- end
- end
-endmodule
diff --git a/atk16_fpga/flash_to_ram.v b/atk16_fpga/flash_to_ram.v
deleted file mode 100644
index 2e0f7b6..0000000
--- a/atk16_fpga/flash_to_ram.v
+++ /dev/null
@@ -1,27 +0,0 @@
-module flash_to_ram(
- // SRAM interface
- output sram_cs_n,
- output sram_rd_n,
- output sram_wr_n,
- output [17:0] sram_addr,
- output [15:0] sram_in,
- input [15:0] sram_out,
-
- // SPI master interface
- output wire spi_cs_n, // Chip select, active low
- output wire spi_clk, // SPI clock
- output wire spi_mosi, // Master Out Slave In
- input wire spi_miso, // Master In Slave Out
- output wire spi_rst_n // Active low reset
-);
-
- // Copy 128KB from flash address 2^20 onwards to SRAM address 0
- // 1. Send a read message to the flash
- // 2. Wait for the flash to respond
- // 3. Write the data to SRAM
- // 4. Repeat until 128KB has been copied
-
-
-
-
-endmodule
diff --git a/atk16_fpga/foo.v b/atk16_fpga/foo.v
new file mode 100644
index 0000000..f6ff33c
--- /dev/null
+++ b/atk16_fpga/foo.v
@@ -0,0 +1,106 @@
+// Top-level module
+module foo (
+ LED1,
+ LED2,
+ BUT1,
+ BUT2,
+ SYSCLK
+);
+ output LED1;
+ output LED2;
+ input BUT1;
+ input BUT2;
+ input SYSCLK;
+
+ // 2-bit counter register
+ reg [1:0] val;
+ initial val = 2'b00;
+
+ // Wires for debounced button signals
+ wire db_but1;
+ wire db_but2;
+
+ // Instantiate debounce modules for each button.
+ // Adjust the DEBOUNCE_LIMIT parameter as needed based on SYSCLK frequency.
+ debounce #(
+ .DEBOUNCE_LIMIT(500000)
+ ) debounce_but1 (
+ .clk(SYSCLK),
+ .button(BUT1),
+ .db_out(db_but1)
+ );
+
+ debounce #(
+ .DEBOUNCE_LIMIT(500000)
+ ) debounce_but2 (
+ .clk(SYSCLK),
+ .button(BUT2),
+ .db_out(db_but2)
+ );
+
+ // Edge detection registers to trigger a single count change per button press.
+ reg prev_db_but1;
+ reg prev_db_but2;
+ initial begin
+ prev_db_but1 = 1'b0;
+ prev_db_but2 = 1'b0;
+ end
+
+ // Drive LEDs from the counter bits.
+ assign LED1 = val[0];
+ assign LED2 = val[1];
+
+ // Synchronous logic to detect rising edges and update counter.
+ always @(posedge SYSCLK) begin
+ // If a rising edge is detected on db_but1, increment the counter.
+ if (db_but1 && !prev_db_but1) val <= val + 1;
+ // Else, if a rising edge is detected on db_but2, decrement the counter.
+ else if (db_but2 && !prev_db_but2) val <= val - 1;
+
+ // Store the current debounced states for edge detection on the next clock.
+ prev_db_but1 <= db_but1;
+ prev_db_but2 <= db_but2;
+ end
+
+endmodule
+
+// Debounce module written in plain Verilog.
+module debounce (
+ clk,
+ button,
+ db_out
+);
+ parameter DEBOUNCE_LIMIT = 500000; // Adjust as needed for your clock frequency.
+ input clk;
+ input button;
+ output db_out;
+ reg db_out;
+
+ reg [31:0] counter; // Counter width chosen to comfortably count up to DEBOUNCE_LIMIT.
+ reg button_sync;
+
+ initial begin
+ counter = 32'd0;
+ db_out = 1'b0;
+ button_sync = 1'b0;
+ end
+
+ always @(posedge clk) begin
+ // First, synchronize the raw button input to the clock domain.
+ button_sync <= button;
+
+ // If the synchronized value equals the debounced output,
+ // reset the counter.
+ if (button_sync == db_out) counter <= 32'd0;
+ else begin
+ // Otherwise, increment the counter.
+ counter <= counter + 1;
+ // If the counter reaches the limit, update the debounced output.
+ if (counter >= DEBOUNCE_LIMIT) begin
+ db_out <= button_sync;
+ counter <= 32'd0;
+ end
+ end
+ end
+
+endmodule
diff --git a/atk16_fpga/ice40hx8k-evb.pcf b/atk16_fpga/ice40hx8k-evb.pcf
index eb63e74..cf75797 100644
--- a/atk16_fpga/ice40hx8k-evb.pcf
+++ b/atk16_fpga/ice40hx8k-evb.pcf
@@ -8,6 +8,10 @@ set_io -nowarn SYSCLK J3 # GBIN6
set_io -nowarn LED1 M12
set_io -nowarn LED2 R16
+# USB<->Serial interface
+set_io -nowarn SERIAL_TX E4
+set_io -nowarn SERIAL_RX B2
+
# Buttons
set_io -nowarn BUT1 K11
set_io -nowarn BUT2 P13
@@ -25,9 +29,9 @@ set_io -nowarn RxD L11
set_io -nowarn TxD T16
# SRAM
-set_io -nowarn SRAM_CS T6
-set_io -nowarn SRAM_OE L9
-set_io -nowarn SRAM_WE T7
+set_io -nowarn SRAM_CS T6 # the schematic says these are inverted
+set_io -nowarn SRAM_OE L9 # but testing says that they are not
+set_io -nowarn SRAM_WE T7 # very weird.
set_io -nowarn SA[0] N6
set_io -nowarn SA[1] T1
set_io -nowarn SA[2] P4
diff --git a/atk16_fpga/isa_idea.md b/atk16_fpga/isa_idea.md
deleted file mode 100644
index ef87189..0000000
--- a/atk16_fpga/isa_idea.md
+++ /dev/null
@@ -1,193 +0,0 @@
-## Registers
-
-There are 16 general-purpose registers, `R0` to `R15`.
-
-`R13` is the flag/condition register.
-`R14` is the program counter.
-`R15` is the stack pointer.
-
-Additionally, `R12` mey be clobbered by the assembler when expanding macros.
-
-## Instructions
-
-XXXX denotes ignored bits.
-
-**HLT** is a halt operation. It stops the processor.
-
-```python
-HLT
-0000 XXXX XXXX XXXX
-```
-
-**ALU** is a 16-bit ALU operation.
-
-- SSS selects the operation to perform
-- LLLL is the left operand and destination register.
-- In register mode (M=0), RRRR is the right operand register.
-- In immediate mode (M=1), the next word is interpreted as a 16-bit immediate value.
-
-```python
-ALU
-# register
-0001 SSSM LLLL RRRR
-# immediate
-0001 SSSM LLLL XXXX
-IIII IIII IIII IIII
-```
-
-**LD** is a load operation.
-
-- LLLL is the destination register.
-- In register mode (M=0), RRRR is the register holding the address to load from.
-- In immediate mode (M=1), the next word is interpreted as a 16-bit address to load from.
-- In direct mode (D=0), the address is used as-is.
-- In indirect mode (D=1), the address is used as a pointer to another address. An indirect load can be thought of as a pointer dereference.
-- In pop mode (P = 1), the RRRR register is incremented before loading. This in conjunction with an indirect load can be used as a stack pop operation. POP is only valid in register mode.
-
-```python
-LD
-# register
-0010 DPXM LLLL RRRR
-# immediate
-0010 DPXM LLLL XXXX
-IIII IIII IIII IIII
-```
-
-**MOV** is a move operation. It copies the value from one register to another.
-
-- LLLL is the destination register.
-- RRRR is the source register.
-
-```python
-MOV
-0011 XXXX LLLL RRRR
-```
-
-**ST** is a store operation.
-
-- LLLL is the source register.
-- In register mode (M=0), RRRR is the register holding the address to store to.
-- In immediate mode (M=1), the next word is interpreted as a 16-bit address to store to.
-- In direct mode (D=0), the address is used as-is.
-- In indirect mode (D=1), the address is used as a pointer to another address. An indirect store can be thought of as a pointer assignment.
-- In push mode (P = 1), the RRRR register is decremented after storing. This in conjunction with an indirect store can be used as a stack push operation. PUSH is only valid in register mode.
-
-```python
-ST
-# register
-0100 DPXM LLLL RRRR
-# immediate
-0100 DPXM LLLL XXXX
-IIII IIII IIII IIII
-```
-
-**BR** is a branch operation.
-
-- FF selects the condition to branch on (carry, overflow, zero, sign).
-- S determines if the selected flag should be set (1) or not set (0).
-- The I octet is an 8-bit signed offset.
-
-```python
-BR
-0101 FFXS IIII IIII
-```
-
-## Calling convention
-
-Arguments are passed in registers R0..R10. The return value is stored in R0.
-The return address is stored on the stack to support nested calls.
-
-Registers `R0..R3` are caller-saved (called function can clobber these registers, calling code must save them on the stack or higher registers if needed).
-Registers `R4..R10` are callee-saved (called function must save these on the stack or lower registers).
-
-## Example assembly
-
-```java
-@at 0x0
- LD R0 0x1
- LD R1 0x2
- ADD R0 R1 ; ADD = macro that expands to ALU 000
-
- BR SIGN UNSET $br_true
-br_false:
- LD R0 0xEE
- HLT
-br_true:
- LD R0 0xFF
- HLT
-```
-
-## Example macro assembly
-
-```java
-@macro ADD lhs rhs
- ALU 0 $lhs $rhs
-@endmacro
-
-@let threshold 0x80
-@if R1 < $threshold ; expands into a SUB and a BR
- LD R0 0x1
- LD R1 0x2
- ADD R0 R1
- HLT
-@else
- ; something
-@endif
-
-@let PC R14
-@let SP R15
-
-@macro SPUSH reg
- ST INDIRECT PUSH $reg $SP
-@endmacro
-
-@macro SPOP reg
- LD INDIRECT POP $reg $SP
-@endmacro
-
-@macro CALL1 fn_lbl arg
-@let ret_addr $gen_uniq ; generate a unique label
- LD R0 $arg ; load argument to R0
- LD R1 $ret_addr ; load return address to R1
- SPUSH R1 ; push return address to stack
- LD $PC $fn_lbl ; jump to function address
-$ret_addr:
- HLT
-@endmacro
-
-@at 0x0
-main:
- CALL1 :fn 0x1
- HLT
-
-; example of an absolute jump
- LD $SP
- $abs_jump_addr
-
-; example of a relative jump
- ADD $PC
- $rel_jump_offset
-
-; idea: syntax for passing the immediate on the same line
- ADD $PC % $rel_jump_offset
-
-; probably should just require the core instructions to be defined "correctly" and have convenience macros for the rest, such as
-
-@macro LOAD reg from
-@c_if immediate $from
- LD $reg
- $from
-$c_else
- LD $reg $from
-$c_endif
-@endmacro
-```
-
-### Assembler concepts
-
-- `R0` to `R15` are register literals.
-- Rows with no indentation are either directives (`@` prefix) or labels (`:` suffix).
-- Rows with indentation are instructions.
-- Comments are prefixed with `;`.
-- Labels can be literals (`my_label:`) or variables (`$my_label_var:`).
-- Compile time variables are defined with `@let` and used with `$`.
diff --git a/atk16_fpga/sram.v b/atk16_fpga/mmodel_sram.v
index 8d62e48..c0e5a0b 100644
--- a/atk16_fpga/sram.v
+++ b/atk16_fpga/mmodel_sram.v
@@ -1,5 +1,4 @@
-`default_nettype none
-`timescale 1 ns / 100 ps
+`default_nettype none `timescale 1 ns / 100 ps
// This is a memory model of the SRAM chip on the iCE40HX8K-EVB.
// Relevant specs:
@@ -7,7 +6,7 @@
// - 18-bit address bus, 16-bit data bus
// - 10 ns read or write cycle time
// - 3 ns output hold from address change
-module sram(
+module mmodel_sram (
input wire cs_n,
input wire wr_n,
input wire rd_n,
@@ -15,7 +14,7 @@ module sram(
input wire [15:0] data_in,
output reg [15:0] data_out
);
- reg [15:0] mem [0:262143]; // 256K x 16 memory
+ reg [15:0] mem[0:262143]; // 256K x 16 memory
always @(cs_n or wr_n or rd_n or addr or data_in) begin
if (cs_n == 1'b1) begin
diff --git a/atk16_fpga/sram_tb.v b/atk16_fpga/mmodel_sram_tb.v
index c734395..364583d 100644
--- a/atk16_fpga/sram_tb.v
+++ b/atk16_fpga/mmodel_sram_tb.v
@@ -2,14 +2,14 @@
`define DUMPSTR(x) `"x.vcd`"
`timescale 1 ns / 100 ps
-module sram_tb();
+module sram_tb ();
reg clk, cs_n, wr_n, rd_n;
- reg [17:0] addr;
- reg [15:0] data_in;
+ reg [17:0] addr;
+ reg [15:0] data_in;
wire [15:0] data_out;
- sram dut(
+ mmodel_sram dut (
.cs_n(cs_n),
.wr_n(wr_n),
.rd_n(rd_n),
@@ -44,7 +44,9 @@ module sram_tb();
if (debug_mem100 === 16'hx) begin
$display("\033[0;32m[PASS]\033[0m Write: data is invalid at time: %0d ns", $time);
end else begin
- $display("\033[0;31m[FAIL]\033[0m Write: data is valid too early, got %04h at time %0d ns", debug_mem100, $time);
+ $display(
+ "\033[0;31m[FAIL]\033[0m Write: data is valid too early, got %04h at time %0d ns",
+ debug_mem100, $time);
failed = 1;
end
@@ -69,7 +71,9 @@ module sram_tb();
if (data_out === 16'hx) begin
$display("\033[0;32m[PASS]\033[0m Read: data is invalid at time: %0d ns", $time);
end else begin
- $display("\033[0;31m[FAIL]\033[0m Read: data is valid too early, got %04h at time %0d ns", data_out, $time);
+ $display(
+ "\033[0;31m[FAIL]\033[0m Read: data is valid too early, got %04h at time %0d ns",
+ data_out, $time);
failed = 1;
end
diff --git a/atk16_fpga/sram_tb.gtkw b/atk16_fpga/sram_tb.gtkw
deleted file mode 100644
index 2b61ccd..0000000
--- a/atk16_fpga/sram_tb.gtkw
+++ /dev/null
@@ -1,56 +0,0 @@
-[*]
-[*] GTKWave Analyzer v3.4.0 (w)1999-2022 BSI
-[*] Sun Dec 29 21:48:27 2024
-[*]
-[dumpfile] "/Users/jantuomi/Projects/Personal/ATK16/atk16_fpga/sram_tb.vcd"
-[dumpfile_mtime] "Sun Dec 29 21:48:18 2024"
-[dumpfile_size] 1502
-[savefile] "/Users/jantuomi/Projects/Personal/ATK16/atk16_fpga/sram_tb.gtkw"
-[timestart] 0
-[size] 1280 600
-[pos] -1 -1
-*-15.091954 14300 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-[markername] AA
-[markername] BB
-[markername] CC
-[markername] DD
-[markername] EE
-[markername] FF
-[markername] GG
-[markername] HH
-[markername] II
-[markername] JJ
-[markername] KK
-[markername] LL
-[markername] MM
-[markername] NN
-[markername] OO
-[markername] PP
-[markername] QQ
-[markername] RR
-[markername] SS
-[markername] TT
-[markername] UU
-[markername] VV
-[markername] WW
-[markername] XX
-[markername] YY
-[markername] ZZ
-[treeopen] sram_tb.
-[sst_width] 240
-[signals_width] 177
-[sst_expanded] 1
-[sst_vpaned_height] 159
-@28
-sram_tb.dut.cs_n
-sram_tb.dut.rd_n
-sram_tb.dut.wr_n
-@22
-sram_tb.dut.last_addr[17:0]
-sram_tb.dut.addr[17:0]
-sram_tb.dut.data_in[15:0]
-sram_tb.dut.data_out[15:0]
-@23
-sram_tb.debug_mem100[15:0]
-[pattern_trace] 1
-[pattern_trace] 0
diff --git a/atk16_fpga/sram_testing.v b/atk16_fpga/sram_testing.v
new file mode 100644
index 0000000..0cf8d19
--- /dev/null
+++ b/atk16_fpga/sram_testing.v
@@ -0,0 +1,89 @@
+`define SRAM_ON 0
+`define SRAM_OFF 1
+
+`define MODE_NONE 0
+`define MODE_READ 1
+`define MODE_WRITE 2
+
+module sram_testing (
+ input wire SYSCLK,
+ output wire LED1,
+ output wire LED2,
+ output wire SRAM_CS,
+ output wire SRAM_OE,
+ output wire SRAM_WE,
+ output reg [17:0] SA, // SRAM address bus
+ inout wire [15:0] SD // SRAM data bus
+);
+
+ reg clk = 0;
+ always @(posedge SYSCLK) begin
+ clk <= ~clk;
+ end
+
+ // Enable SRAM chip select
+ assign SRAM_CS = `SRAM_ON;
+
+ wire [15:0] address = 16'hdead;
+ wire [15:0] write_datum = 16'hbeef;
+ reg [15:0] read_datum;
+
+ assign LED1 = read_datum == write_datum;
+ assign LED2 = LED1;
+
+ reg [ 1:0] mode = `MODE_NONE;
+ reg [15:0] SD_write;
+ wire [15:0] SD_read;
+ assign SD = mode == `MODE_WRITE ? SD_write : 16'hz;
+ assign SD_read = mode == `MODE_READ ? SD : 16'hz;
+ assign SRAM_WE = mode == `MODE_WRITE ? `SRAM_ON : `SRAM_OFF;
+ assign SRAM_OE = mode == `MODE_READ ? `SRAM_ON : `SRAM_OFF;
+
+ // write and read back datum from SRAM
+ reg [15:0] state = 0;
+ always @(posedge clk) begin
+ case (state)
+ // write the data
+ 0: begin
+ mode <= `MODE_WRITE;
+ SA <= {2'b0, address};
+ state <= state + 1;
+ end
+ 1: begin
+ SD_write <= write_datum;
+ state <= state + 1;
+ end
+ // write something else to jumble SA and SD, in 2 cycles
+ 2: begin
+ SA <= {2'b0, address} + 10;
+ state <= state + 1;
+ end
+ 3: begin
+ SD_write <= 16'hcafe;
+ state <= state + 1;
+ end
+ // read in the jumbled data
+ 4: begin
+ mode <= `MODE_READ;
+ state <= state + 1;
+ end
+ 5: begin
+ read_datum <= SD_read;
+ state <= state + 1;
+ end
+ // read back the original datum
+ 6: begin
+ SA <= {2'b0, address};
+ state <= state + 1;
+ end
+ 7: begin
+ read_datum <= SD_read;
+ state <= state + 1;
+ end
+ default: begin
+ // halt
+ end
+ endcase
+ end
+
+endmodule
diff --git a/atk16_fpga/top.v b/atk16_fpga/top.v
index 065d034..df53491 100644
--- a/atk16_fpga/top.v
+++ b/atk16_fpga/top.v
@@ -1,38 +1,157 @@
-`default_nettype none
-
-`define PH_COPY_IMG 1'b0
-`define PH_RUN_IMG 1'b1
+// UART transmitter module.
+// Transmits a frame: start bit (0), 8 data bits (LSB first), stop bit (1)
+// at a baud rate determined by CLKS_PER_BIT (here: 10417 for 100MHz/9600)
+module uart_tx (
+ input clk, // system clock: 100 MHz
+ input rst, // synchronous reset (active high)
+ input start, // one-cycle pulse to start transmission
+ input [7:0] data, // data byte to send
+ output reg tx, // serial output
+ output reg busy // high while transmitting the byte
+);
+ // 100e6/9600 ≈ 10417 cycles per bit.
+ parameter CLKS_PER_BIT = 10417;
-module top(
- input wire SYSCLK,
- input wire BUT1,
- output wire LED1,
+ reg [13:0] clk_count; // counter for baud tick (14 bits is enough)
+ reg [ 3:0] bit_index; // counts from 0 to 9 (10 bits total: start, 8 data, stop)
+ reg [ 9:0] tx_frame; // complete frame: {stop bit, data[7:0], start bit}
- output SRAM_CS, // inverted
- output SRAM_OE, // inverted
- output SRAM_WE, // inverted
- output [17:0] SA,
- inout [15:0] SD,
+ always @(posedge clk) begin
+ if (rst) begin
+ busy <= 1'b0;
+ tx <= 1'b1; // idle state is high
+ clk_count <= 0;
+ bit_index <= 0;
+ end else begin
+ if (!busy) begin
+ if (start) begin
+ // Load frame: start bit (0), 8 data bits (LSB first), stop bit (1)
+ tx_frame <= {1'b1, data, 1'b0};
+ busy <= 1'b1;
+ clk_count <= 0;
+ bit_index <= 0;
+ tx <= 1'b0; // send start bit immediately
+ end else begin
+ tx <= 1'b1; // remain idle
+ end
+ end else begin
+ // When busy, count clocks for each bit period.
+ if (clk_count < CLKS_PER_BIT - 1) clk_count <= clk_count + 1;
+ else begin
+ clk_count <= 0;
+ bit_index <= bit_index + 1;
+ if (bit_index < 9) tx <= tx_frame[bit_index+1];
+ else begin
+ busy <= 1'b0;
+ tx <= 1'b1; // return to idle
+ end
+ end
+ end
+ end
+ end
+endmodule
- input [3:0] INT
+// Top-level module for the iCE40 HX8K FPGA.
+// This module instantiates the UART transmitter and sends the bytes for
+// "Hello" and a newline character, then stops.
+module top (
+ input SYSCLK, // 100 MHz system clock
+ input SERIAL_RX, // Unused in this design; available as a pin
+ output SERIAL_TX // UART transmit output
);
- reg phase = `PH_COPY_IMG;
- cu cu_inst(
- .clk(SYSCLK),
- .rst(BUT1),
+ // Create a simple synchronous reset signal.
+ // Here, we use a counter that holds reset high for a few hundred cycles.
+ reg [7:0] reset_cnt;
+ reg sys_rst;
+ always @(posedge SYSCLK) begin
+ if (reset_cnt != 8'd255) begin
+ reset_cnt <= reset_cnt + 1;
+ sys_rst <= 1'b1;
+ end else begin
+ sys_rst <= 1'b0;
+ end
+ end
- .sram_cs_n(SRAM_CS),
- .sram_rd_n(SRAM_OE),
- .sram_wr_n(SRAM_WE),
- .sram_addr(SA[15:0]),
- .sram_in(SD),
- .sram_out(SD),
+ // ROM for the message "Hello\n" (6 bytes)
+ reg [7:0] message[0:5];
+ initial begin
+ message[0] = "H"; // ASCII 72
+ message[1] = "e"; // ASCII 101
+ message[2] = "l"; // ASCII 108
+ message[3] = "l"; // ASCII 108
+ message[4] = "o"; // ASCII 111
+ message[5] = "\n"; // ASCII 10 (newline)
+ end
- .int_lines(INT)
- );
+ // Pointer to index through the message.
+ reg [2:0] index;
+ // One-cycle pulse to trigger transmission.
+ reg start_tx;
+ // Data byte to be transmitted.
+ reg [7:0] data_to_send;
+ // Busy flag from the transmitter.
+ wire tx_busy;
+
+ // State machine states.
+ localparam IDLE = 2'd0, WAIT_BUSY = 2'd1, WAIT_IDLE = 2'd2, DONE = 2'd3;
+ reg [1:0] state;
+
+ // Synchronous state machine with reset.
+ always @(posedge SYSCLK) begin
+ if (sys_rst) begin
+ // Initialize on reset.
+ state <= IDLE;
+ index <= 0;
+ start_tx <= 1'b0;
+ data_to_send <= 8'd0;
+ end else begin
+ case (state)
+ IDLE: begin
+ start_tx <= 1'b0;
+ if (index < 3'd6) begin
+ if (!tx_busy) begin
+ data_to_send <= message[index];
+ start_tx <= 1'b1; // trigger transmission
+ state <= WAIT_BUSY;
+ end
+ end else begin
+ state <= DONE;
+ end
+ end
- assign SA[17:16] = 2'b0;
- assign LED1 = BUT1;
+ // After issuing the start pulse, wait for tx_busy to go high.
+ WAIT_BUSY: begin
+ start_tx <= 1'b0; // ensure pulse is only one cycle.
+ if (tx_busy) state <= WAIT_IDLE;
+ end
+
+ // Wait for the transmitter to finish sending the byte.
+ WAIT_IDLE: begin
+ if (!tx_busy) begin
+ index <= index + 1; // move to next byte
+ state <= IDLE;
+ end
+ end
+
+ DONE: begin
+ start_tx <= 1'b0;
+ // Remain in DONE state; no further transmissions.
+ end
+
+ default: state <= IDLE;
+ endcase
+ end
+ end
+
+ // Instantiate the UART transmitter.
+ uart_tx uart_inst (
+ .clk (SYSCLK),
+ .rst (sys_rst), // Use our synchronous reset signal.
+ .start(start_tx), // One-cycle pulse to start transmission.
+ .data (data_to_send),
+ .tx (SERIAL_TX),
+ .busy (tx_busy)
+ );
endmodule