diff --git a/4-Infrastructure/hardware/Blitter6502OISC_small.v b/4-Infrastructure/hardware/Blitter6502OISC_small.v new file mode 100644 index 00000000..da9d849a --- /dev/null +++ b/4-Infrastructure/hardware/Blitter6502OISC_small.v @@ -0,0 +1,322 @@ +//------------------------------------------------------------------------------ +// 6502 OISC Blitter — 0D Scalar Proof Engine +// Target: Sipeed Tang Nano 9K (GW1NR-LV9QN88PC6/I5) +// +// One-Instruction-Set Computer with 6502 memory map. +// Single instruction: SUBLEQ (Subtract and Branch if Less-or-Equal). +// +// SUBLEQ src dst next: +// MEM[dst] <= MEM[dst] - MEM[src] +// if MEM[dst] <= 0 then PC <= next else PC <= PC + 6 +// +// The "blitter" is built from SUBLEQ loops — block memory operations +// that iterate over memory, one cell at a time (0D scalar). +// +// Memory map: +// $0000-$00FF : Zero page (registers, blitter params) +// $0100-$01FF : Stack +// $0200-$02FF : S3C sqrt LUT (256 entries) +// $0300-$03FF : Blitter program segment +// $0400-$7FFF : General memory +// $8000-$FFFF : I/O mapped (LEDs, UART, I2S) +//------------------------------------------------------------------------------ + +module Blitter6502OISC ( + input wire clk, + input wire rst_n, + input wire start, + output reg busy, + output reg [5:0] led, + output reg uart_tx, + // Memory interface (for external loading) + input wire mem_we, + input wire [11:0] mem_addr, + input wire [7:0] mem_wdata, + output wire [7:0] mem_rdata +); + + //========================================================================== + // Parameters + //========================================================================== + localparam MEM_SIZE = 4096; + localparam MAX_CYCLES = 24'd1000000; // ~37ms at 27MHz + + //========================================================================== + // Memory (64K x 8-bit) + // Implemented as block RAM (BSRAM) on GW1NR + //========================================================================== + reg [7:0] mem [0:MEM_SIZE-1]; + reg [15:0] mem_raddr; + + // Dual-port memory interface + // Port A: CPU access (synchronous read, synchronous write) + // Port B: External loader access + always @(posedge clk) begin + if (mem_we) + mem[mem_addr] <= mem_wdata; + mem_raddr <= mem_addr; + end + assign mem_rdata = mem[mem_raddr]; + + //========================================================================== + // CPU State + //========================================================================== + reg [11:0] pc; + reg [7:0] a_reg; // Accumulator (mirrors $0000) + reg [7:0] x_reg; // X register (mirrors $0001) + reg [7:0] y_reg; // Y register (mirrors $0002) + reg [23:0] cycle_cnt; + reg halted; + + // Instruction decode registers + reg [11:0] src_addr; + reg [11:0] dst_addr; + reg [11:0] next_addr; + reg [7:0] src_val; + reg [7:0] dst_val; + reg [7:0] result; + + // State machine + localparam ST_IDLE = 4'd0; + localparam ST_FETCH_S0 = 4'd1; + localparam ST_FETCH_S1 = 4'd2; + localparam ST_FETCH_D0 = 4'd3; + localparam ST_FETCH_D1 = 4'd4; + localparam ST_FETCH_N0 = 4'd5; + localparam ST_FETCH_N1 = 4'd6; + localparam ST_READ_SRC = 4'd7; + localparam ST_READ_DST = 4'd8; + localparam ST_EXECUTE = 4'd9; + localparam ST_WRITE = 4'd10; + localparam ST_BRANCH = 4'd11; + localparam ST_HALT = 4'd12; + + reg [3:0] state; + + //========================================================================== + // S3C sqrt LUT (preloaded at $0200-$02FF) + // 256 entries: sqrtLUT8[n] = floor(sqrt(n)) + //========================================================================== + initial begin + // First 32 entries (rest loaded via external interface or defaults) + mem[16'h0200] = 8'd0; mem[16'h0201] = 8'd1; mem[16'h0202] = 8'd1; mem[16'h0203] = 8'd1; + mem[16'h0204] = 8'd2; mem[16'h0205] = 8'd2; mem[16'h0206] = 8'd2; mem[16'h0207] = 8'd2; + mem[16'h0208] = 8'd2; mem[16'h0209] = 8'd3; mem[16'h020A] = 8'd3; mem[16'h020B] = 8'd3; + mem[16'h020C] = 8'd3; mem[16'h020D] = 8'd3; mem[16'h020E] = 8'd3; mem[16'h020F] = 8'd3; + mem[16'h0210] = 8'd4; mem[16'h0211] = 8'd4; mem[16'h0212] = 8'd4; mem[16'h0213] = 8'd4; + mem[16'h0214] = 8'd4; mem[16'h0215] = 8'd4; mem[16'h0216] = 8'd4; mem[16'h0217] = 8'd4; + mem[16'h0218] = 8'd4; mem[16'h0219] = 8'd5; mem[16'h021A] = 8'd5; mem[16'h021B] = 8'd5; + mem[16'h021C] = 8'd5; mem[16'h021D] = 8'd5; mem[16'h021E] = 8'd5; mem[16'h021F] = 8'd5; + end + + //========================================================================== + // State Machine + //========================================================================== + always @(posedge clk or negedge rst_n) begin + if (!rst_n) begin + pc <= 16'h0300; // Program starts at $0300 + a_reg <= 8'd0; + x_reg <= 8'd0; + y_reg <= 8'd0; + cycle_cnt <= 24'd0; + halted <= 1'b0; + busy <= 1'b0; + led <= 6'b000000; + state <= ST_IDLE; + end else begin + case (state) + ST_IDLE: begin + if (start) begin + busy <= 1'b1; + pc <= 16'h0300; + cycle_cnt <= 24'd0; + halted <= 1'b0; + state <= ST_FETCH_S0; + end + end + + // Fetch src address (2 bytes, little-endian) + ST_FETCH_S0: begin + src_addr[7:0] <= mem[pc]; + state <= ST_FETCH_S1; + end + ST_FETCH_S1: begin + src_addr[15:8] <= mem[pc + 16'd1]; + state <= ST_FETCH_D0; + end + + // Fetch dst address + ST_FETCH_D0: begin + dst_addr[7:0] <= mem[pc + 16'd2]; + state <= ST_FETCH_D1; + end + ST_FETCH_D1: begin + dst_addr[15:8] <= mem[pc + 16'd3]; + state <= ST_FETCH_N0; + end + + // Fetch next address + ST_FETCH_N0: begin + next_addr[7:0] <= mem[pc + 16'd4]; + state <= ST_FETCH_N1; + end + ST_FETCH_N1: begin + next_addr[15:8] <= mem[pc + 16'd5]; + state <= ST_READ_SRC; + end + + // Read src and dst values + ST_READ_SRC: begin + src_val <= mem[src_addr]; + state <= ST_READ_DST; + end + ST_READ_DST: begin + dst_val <= mem[dst_addr]; + state <= ST_EXECUTE; + end + + // Execute: dst = dst - src (unsigned wrap, check signed <= 0) + ST_EXECUTE: begin + result <= dst_val - src_val; + cycle_cnt <= cycle_cnt + 24'd1; + state <= ST_WRITE; + end + + // Write result back + ST_WRITE: begin + mem[dst_addr] <= result; + state <= ST_BRANCH; + end + + // Branch if result <= 0 (signed interpretation) + // In 8-bit signed: negative if MSB is 1, zero if all bits 0 + ST_BRANCH: begin + if (result[7] == 1'b1 || result == 8'd0) begin + // Result <= 0: branch to next_addr + pc <= next_addr; + end else begin + // Result > 0: fall through + pc <= pc + 16'd6; + end + + // Check halt conditions + if (cycle_cnt >= MAX_CYCLES) begin + halted <= 1'b1; + busy <= 1'b0; + led <= 6'b111111; // Error: cycle exhausted + state <= ST_HALT; + end else if (next_addr == 16'hFFFF) begin + // Halt opcode: next_addr = $FFFF + halted <= 1'b1; + busy <= 1'b0; + led <= {a_reg[1:0], x_reg[1:0], y_reg[1:0]}; // Success pattern + state <= ST_HALT; + end else begin + state <= ST_FETCH_S0; + end + end + + ST_HALT: begin + // Remain halted until reset + halted <= 1'b1; + busy <= 1'b0; + end + + default: state <= ST_IDLE; + endcase + end + end + + // UART telemetry: when halted, send a_reg as status byte + reg [3:0] uart_bit_cnt; + reg [15:0] uart_div; + reg [9:0] uart_shift; + reg uart_active; + + localparam UART_DIV = 16'd234; // ~115200 baud at 27MHz + + always @(posedge clk or negedge rst_n) begin + if (!rst_n) begin + uart_tx <= 1'b1; + uart_bit_cnt <= 4'd0; + uart_div <= 16'd0; + uart_shift <= 10'b0; + uart_active <= 1'b0; + end else begin + if (halted && !uart_active) begin + // Start UART transmission with a_reg as payload + uart_shift <= {1'b1, a_reg, 1'b0}; // stop, data, start + uart_active <= 1'b1; + uart_bit_cnt <= 4'd0; + uart_div <= 16'd0; + end + + if (uart_active) begin + if (uart_div >= UART_DIV) begin + uart_div <= 16'd0; + uart_tx <= uart_shift[0]; + uart_shift <= {1'b1, uart_shift[9:1]}; + uart_bit_cnt <= uart_bit_cnt + 4'd1; + if (uart_bit_cnt >= 4'd9) begin + uart_active <= 1'b0; + uart_tx <= 1'b1; + end + end else begin + uart_div <= uart_div + 16'd1; + end + end + end + end + +endmodule + +//------------------------------------------------------------------------------ +// Blitter Top-Level Wrapper with Clock/Reset +//------------------------------------------------------------------------------ +module Blitter6502OISCTop ( + input wire clk, // 27 MHz oscillator + input wire rst_n, // Active-low reset + input wire user_btn, // Button to start blitter + output wire [5:0] led, + output wire uart_tx +); + + wire btn_pressed = ~user_btn; + reg btn_sync1, btn_sync2, btn_rise; + reg [19:0] debounce_cnt; + + // Button debounce (same as SparkleTangNano9KTop) + always @(posedge clk or negedge rst_n) begin + if (!rst_n) begin + btn_sync1 <= 1'b0; + btn_sync2 <= 1'b0; + btn_rise <= 1'b0; + debounce_cnt <= 20'd0; + end else begin + btn_sync1 <= btn_pressed; + btn_sync2 <= btn_sync1; + if (btn_sync2) begin + if (debounce_cnt < 20'd500000) + debounce_cnt <= debounce_cnt + 20'd1; + end else begin + debounce_cnt <= 20'd0; + end + btn_rise <= (debounce_cnt >= 20'd500000) && !btn_sync2; + end + end + + // Blitter instance + Blitter6502OISC blitter ( + .clk(clk), + .rst_n(rst_n), + .start(btn_rise), + .busy(), + .led(led), + .uart_tx(uart_tx), + .mem_we(1'b0), + .mem_addr(12'd0), + .mem_wdata(8'd0), + .mem_rdata() + ); + +endmodule diff --git a/4-Infrastructure/hardware/build_research_stack.sh b/4-Infrastructure/hardware/build_research_stack.sh index e69895a4..91f486af 100755 --- a/4-Infrastructure/hardware/build_research_stack.sh +++ b/4-Infrastructure/hardware/build_research_stack.sh @@ -21,7 +21,7 @@ yosys -p " voltage_mode_controller.v \ scale_space_bram.v \ highs_pivot_accelerator.v \ - sparkle/tangnano9k/Blitter6502OISC.v \ + Blitter6502OISC_small.v \ research_stack_top.v; synth_gowin -top ${TOP} -json ${JSON}; stat diff --git a/4-Infrastructure/hardware/research_stack_tangnano9k.cst b/4-Infrastructure/hardware/research_stack_tangnano9k.cst index 4d685546..812d90c7 100644 --- a/4-Infrastructure/hardware/research_stack_tangnano9k.cst +++ b/4-Infrastructure/hardware/research_stack_tangnano9k.cst @@ -1,14 +1,36 @@ -# Research Stack Tang Nano 9K Constraint File -# Target: GW1NR-LV9QN88PC6/I5 +// Research Stack Tang Nano 9K Constraint File +// Board: Sipeed Tang Nano 9K +// FPGA: Gowin GW1NR-LV9QN88PC6/I5 -IO_PORT "clk" LOC=52 | IOSTANDARD=LVCMOS33; -IO_PORT "rst_n" LOC=3 | IOSTANDARD=LVCMOS33; -IO_PORT "user_btn" LOC=4 | IOSTANDARD=LVCMOS33; -IO_PORT "led[0]" LOC=10 | IOSTANDARD=LVCMOS18; -IO_PORT "led[1]" LOC=11 | IOSTANDARD=LVCMOS18; -IO_PORT "led[2]" LOC=13 | IOSTANDARD=LVCMOS18; -IO_PORT "led[3]" LOC=14 | IOSTANDARD=LVCMOS18; -IO_PORT "led[4]" LOC=15 | IOSTANDARD=LVCMOS18; -IO_PORT "led[5]" LOC=16 | IOSTANDARD=LVCMOS18; -IO_PORT "uart_tx" LOC=17 | IOSTANDARD=LVCMOS33; -IO_PORT "uart_rx" LOC=18 | IOSTANDARD=LVCMOS33; +IO_LOC "clk" 52; +IO_PORT "clk" IO_TYPE=LVCMOS33 PULL_MODE=NONE; + +IO_LOC "rst_n" 4; +IO_PORT "rst_n" IO_TYPE=LVCMOS33 PULL_MODE=UP; + +IO_LOC "user_btn" 3; +IO_PORT "user_btn" IO_TYPE=LVCMOS33 PULL_MODE=UP; + +IO_LOC "led[0]" 10; +IO_PORT "led[0]" IO_TYPE=LVCMOS18 PULL_MODE=NONE; + +IO_LOC "led[1]" 11; +IO_PORT "led[1]" IO_TYPE=LVCMOS18 PULL_MODE=NONE; + +IO_LOC "led[2]" 13; +IO_PORT "led[2]" IO_TYPE=LVCMOS18 PULL_MODE=NONE; + +IO_LOC "led[3]" 14; +IO_PORT "led[3]" IO_TYPE=LVCMOS18 PULL_MODE=NONE; + +IO_LOC "led[4]" 15; +IO_PORT "led[4]" IO_TYPE=LVCMOS18 PULL_MODE=NONE; + +IO_LOC "led[5]" 16; +IO_PORT "led[5]" IO_TYPE=LVCMOS18 PULL_MODE=NONE; + +IO_LOC "uart_tx" 17; +IO_PORT "uart_tx" IO_TYPE=LVCMOS33 PULL_MODE=NONE; + +IO_LOC "uart_rx" 18; +IO_PORT "uart_rx" IO_TYPE=LVCMOS33 PULL_MODE=UP; diff --git a/4-Infrastructure/hardware/research_stack_top.v b/4-Infrastructure/hardware/research_stack_top.v index 20a27dd5..61c89831 100644 --- a/4-Infrastructure/hardware/research_stack_top.v +++ b/4-Infrastructure/hardware/research_stack_top.v @@ -49,7 +49,7 @@ module research_stack_top ( wire [5:0] cpu_led; wire cpu_uart_tx; wire [7:0] cpu_rdata; - wire [15:0] cpu_mem_addr; + wire [11:0] cpu_mem_addr; wire [7:0] cpu_mem_wdata; wire cpu_mem_we; @@ -135,7 +135,7 @@ module research_stack_top ( blitter_memory_map mem_map ( .clk(clk), .rst_n(rst_n), - .addr(cpu_mem_addr), + .addr({4'b0, cpu_mem_addr}), // pad 12-bit to 16-bit .wdata(cpu_mem_wdata), .we(cpu_mem_we), .rdata(map_rdata),