diff options
| author | Jan Tuomi <jan@jantuomi.fi> | 2025-03-18 18:38:44 +0200 |
|---|---|---|
| committer | Jan Tuomi <jan@jantuomi.fi> | 2025-03-18 18:38:44 +0200 |
| commit | 69de37cdb25563f6cbd2a3c1a338b1e0dd47b3a4 (patch) | |
| tree | c1895044fd0051f919a989460be3ac39a6ab09b8 /atk16_fpga | |
| parent | 06e8b8dbadf769507abf5368caee253ded139adc (diff) | |
Start reworking fpga impl
Diffstat (limited to 'atk16_fpga')
| -rw-r--r-- | atk16_fpga/.gitignore | 11 | ||||
| -rw-r--r-- | atk16_fpga/Makefile | 24 | ||||
| -rw-r--r-- | atk16_fpga/alu.v | 35 | ||||
| -rw-r--r-- | atk16_fpga/alu_gtkw_translate_sel.txt | 8 | ||||
| -rw-r--r-- | atk16_fpga/alu_tb.gtkw | 54 | ||||
| -rw-r--r-- | atk16_fpga/alu_tb.v | 73 | ||||
| -rw-r--r-- | atk16_fpga/bram.v | 37 | ||||
| -rw-r--r-- | atk16_fpga/bram_fsm.v | 58 | ||||
| -rw-r--r-- | atk16_fpga/bram_fsm_tb.gtkw | 32 | ||||
| -rw-r--r-- | atk16_fpga/bram_fsm_tb.v | 84 | ||||
| -rw-r--r-- | atk16_fpga/cu.v | 524 | ||||
| -rw-r--r-- | atk16_fpga/cu_tb.gtkw | 73 | ||||
| -rw-r--r-- | atk16_fpga/cu_tb.v | 587 | ||||
| -rw-r--r-- | atk16_fpga/flash.v | 60 | ||||
| -rw-r--r-- | atk16_fpga/flash_to_ram.v | 27 | ||||
| -rw-r--r-- | atk16_fpga/foo.v | 106 | ||||
| -rw-r--r-- | atk16_fpga/ice40hx8k-evb.pcf | 10 | ||||
| -rw-r--r-- | atk16_fpga/isa_idea.md | 193 | ||||
| -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.gtkw | 56 | ||||
| -rw-r--r-- | atk16_fpga/sram_testing.v | 89 | ||||
| -rw-r--r-- | atk16_fpga/top.v | 175 |
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 |
