From 4dd69d2913152d80af58d7d68fd195c031180606 Mon Sep 17 00:00:00 2001 From: vin Date: Thu, 20 Nov 2025 11:01:48 -0500 Subject: initial golden rtl --- .gitignore | 3 + rtl/lisp_coproc.sv | 268 ++++++++++++++++++++++++++++++++++++++++++++++++++ rtl/tb_lisp_coproc.sv | 171 ++++++++++++++++++++++++++++++++ 3 files changed, 442 insertions(+) create mode 100644 .gitignore create mode 100644 rtl/lisp_coproc.sv create mode 100644 rtl/tb_lisp_coproc.sv diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..472c55f --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +rtl/lisp_coproc_sim +*~ +#* \ No newline at end of file diff --git a/rtl/lisp_coproc.sv b/rtl/lisp_coproc.sv new file mode 100644 index 0000000..bd3057a --- /dev/null +++ b/rtl/lisp_coproc.sv @@ -0,0 +1,268 @@ +module lisp_coproc ( + input wire clk, + input wire rst, + input wire cs, + input wire rw, // 0=Write, 1=Read + input wire [2:0] addr, + input wire [7:0] data_in, + output reg [7:0] data_out +); + + // FSM States (One-Hot Encoding) + parameter [4:0] RESET = 5'b00001; + parameter [4:0] IDLE = 5'b00010; + parameter [4:0] DECODE = 5'b00100; + parameter [4:0] EXECUTE = 5'b01000; + parameter [4:0] WRITEBACK= 5'b10000; + + // Internal Registers + reg [4:0] state, next_state; + reg [7:0] opcode_reg, arg1_reg, arg2_reg, result_reg, status_reg; + reg [7:0] heap [0:15]; // 16 entries x 8 bits + + // FIX: Bump allocator must be 5 bits to hold the value '16' (Full) + // without wrapping around to 0. + reg [4:0] bump_alloc; + + // Status Register Bits + wire busy = (state != IDLE); + reg err_heap_full, err_type, carry, zero; + + // ALU Signals + reg [5:0] alu_a, alu_b; + + // Temporary registers for operations + reg [7:0] temp_result; + reg [3:0] temp_ptr; + + // FSM State Transition + always @(posedge clk or posedge rst) begin + if (rst) begin + state <= RESET; + end else begin + state <= next_state; + end + end + + // FSM Combinational Logic + always @(*) begin + next_state = state; + + case (state) + RESET: begin + next_state = IDLE; + end + + IDLE: begin + if (cs && !rw && addr == 3'h0) begin // Writing to OPCODE triggers operation + next_state = DECODE; + end + end + + DECODE: begin + next_state = EXECUTE; + end + + EXECUTE: begin + next_state = WRITEBACK; + end + + WRITEBACK: begin + next_state = IDLE; + end + + default: begin + next_state = IDLE; + end + endcase + end + + // Register File and Memory Interface + always @(posedge clk or posedge rst) begin + if (rst) begin + opcode_reg <= 8'h00; + arg1_reg <= 8'h00; + arg2_reg <= 8'h00; + result_reg <= 8'h00; + status_reg <= 8'h00; + bump_alloc <= 5'h00; // Reset 5-bit register + err_heap_full <= 1'b0; + err_type <= 1'b0; + carry <= 1'b0; + zero <= 1'b0; + end else begin + // Memory-mapped register writes + if (cs && !rw) begin + case (addr) + 3'h0: opcode_reg <= data_in; + 3'h1: arg1_reg <= data_in; + 3'h2: arg2_reg <= data_in; + 3'h3: result_reg <= data_in; // Direct write to result + 3'h4: status_reg <= data_in; // Direct write to status + endcase + end + + // FSM State-specific operations + case (state) + RESET: begin + // Clear heap on reset + integer i; + for (i = 0; i < 16; i = i + 1) begin + heap[i] <= 8'h00; + end + bump_alloc <= 5'h00; + opcode_reg <= 8'h00; + arg1_reg <= 8'h00; + arg2_reg <= 8'h00; + result_reg <= 8'h00; + status_reg <= 8'h00; + end + + IDLE: begin + // Only clear flags when a NEW operation starts. + if (cs && !rw && addr == 3'h0) begin + err_heap_full <= 1'b0; + err_type <= 1'b0; + carry <= 1'b0; + zero <= 1'b0; + end + end + + EXECUTE: begin + // Assign ALU inputs for ADD operation using BLOCKING assignment + alu_a = arg1_reg[5:0]; + alu_b = arg2_reg[5:0]; + + case (opcode_reg) + // CONS operation + 8'h01: begin + // 5-bit arithmetic: 16 + 2 = 18. 18 > 16 is TRUE. + if (bump_alloc + 2 > 16) begin + err_heap_full <= 1'b1; + end else begin + // Store ARG1 and ARG2 in heap + heap[bump_alloc[3:0]] <= arg1_reg; + heap[bump_alloc[3:0] + 1] <= arg2_reg; + // Return CONS tag with pointer as value + temp_ptr <= bump_alloc[3:0]; + bump_alloc <= bump_alloc + 2; + end + end + + // CAR operation + 8'h02: begin + if (arg1_reg[7:6] != 2'b11) begin // Not a CONS + err_type <= 1'b1; + end else begin + temp_result <= heap[arg1_reg[3:0]]; + end + end + + // CDR operation + 8'h03: begin + if (arg1_reg[7:6] != 2'b11) begin // Not a CONS + err_type <= 1'b1; + end else begin + temp_result <= heap[arg1_reg[3:0] + 1]; + end + end + + // ATOM operation + 8'h04: begin + if (arg1_reg[7:6] == 2'b11) begin // Is a CONS + temp_result <= 8'h00; // NIL + end else begin + temp_result <= 8'h41; // 'T' (01_000001) + end + end + + // EQ operation + 8'h05: begin + if (arg1_reg == arg2_reg) begin + temp_result <= 8'h41; // 'T' (01_000001) + zero <= 1'b1; + end else begin + temp_result <= 8'h00; // NIL + end + end + + // ADD operation + 8'h06: begin + if (arg1_reg[7:6] != 2'b10 || arg2_reg[7:6] != 2'b10) begin // Not both NUMBERs + err_type <= 1'b1; + end else begin + if ((alu_a + alu_b) > 6'd63) begin + carry <= 1'b1; + end + if ((alu_a + alu_b) == 6'd0) begin + zero <= 1'b1; + end + temp_result <= {2'b10, alu_a + alu_b}; // NUMBER tag with sum + end + end + endcase + end + + WRITEBACK: begin + // Write the result based on the operation + case (opcode_reg) + 8'h01: begin // CONS + if (!err_heap_full) begin + // Tag [7:6] must be set correctly. + result_reg <= {2'b11, 2'b00, temp_ptr}; + end + end + + 8'h02: begin // CAR + if (!err_type) begin + result_reg <= temp_result; + end + end + + 8'h03: begin // CDR + if (!err_type) begin + result_reg <= temp_result; + end + end + + 8'h04: begin // ATOM + result_reg <= temp_result; + end + + 8'h05: begin // EQ + result_reg <= temp_result; + end + + 8'h06: begin // ADD + if (!err_type) begin + result_reg <= temp_result; + end + end + endcase + end + endcase + end + end + + // Update status register + always @(*) begin + status_reg = {3'b000, zero, carry, err_type, err_heap_full, busy}; + end + + // Output Logic + always @(*) begin + if (cs && rw) begin + case (addr) + 3'h0: data_out = opcode_reg; + 3'h1: data_out = arg1_reg; + 3'h2: data_out = arg2_reg; + 3'h3: data_out = result_reg; + 3'h4: data_out = status_reg; + default: data_out = 8'h00; + endcase + end else begin + data_out = 8'hZZ; // High impedance when not reading + end + end + +endmodule diff --git a/rtl/tb_lisp_coproc.sv b/rtl/tb_lisp_coproc.sv new file mode 100644 index 0000000..6c39aa4 --- /dev/null +++ b/rtl/tb_lisp_coproc.sv @@ -0,0 +1,171 @@ +`timescale 1ns/1ps + +module tb_lisp_coproc; + + // Testbench signals + reg clk; + reg rst; + reg cs; + reg rw; // 0=Write, 1=Read + reg [2:0] addr; + reg [7:0] data_in; + wire [7:0] data_out; + + // Test variables + reg [7:0] read_data; + integer i; + + // Instantiate the DUT + lisp_coproc dut ( + .clk(clk), + .rst(rst), + .cs(cs), + .rw(rw), + .addr(addr), + .data_in(data_in), + .data_out(data_out) + ); + + // Clock generation + initial begin + clk = 0; + forever #5 clk = ~clk; + end + + // CPU Write Task + task cpu_write; + input [2:0] addr_in; + input [7:0] data_in_in; + begin + @(posedge clk); + cs = 1'b1; + rw = 1'b0; // Write + addr = addr_in; + data_in = data_in_in; + @(posedge clk); + cs = 1'b0; + end + endtask + + // CPU Read Task + task cpu_read; + input [2:0] addr_in; + output [7:0] data_out_out; + reg [7:0] status; + begin + // Poll until not busy + do begin + @(posedge clk); + cs = 1'b1; + rw = 1'b1; // Read + addr = 3'h4; // Status register + @(posedge clk); + status = data_out; + cs = 1'b0; + end while (status[0]); // Check BUSY bit + + // Read the requested address + @(posedge clk); + cs = 1'b1; + rw = 1'b1; // Read + addr = addr_in; + @(posedge clk); + data_out_out = data_out; + cs = 1'b0; + end + endtask + + // Test Sequence + initial begin + // Initialize signals + rst = 1'b1; + cs = 1'b0; + rw = 1'b0; + addr = 3'h0; + data_in = 8'h00; + + // Apply reset + #20; + rst = 1'b0; + #20; + + // Test 1: Reset Test + $display("Test 1: Reset Test"); + cpu_read(3'h4, read_data); // Read STATUS + $display("STATUS after reset: 0x%02h (expected: 0x00)", read_data); + if (read_data != 8'h00) $display("ERROR: Reset test failed"); + + // Test 2: ADD Test + $display("\nTest 2: ADD Test"); + cpu_write(3'h1, 8'h85); // ARG1 = NUMBER(5) + cpu_write(3'h2, 8'h86); // ARG2 = NUMBER(6) + cpu_write(3'h0, 8'h06); // OPCODE = ADD + + cpu_read(3'h3, read_data); // Read RESULT + $display("ADD Result: 0x%02h (expected: 0x8B, NUMBER(11))", read_data); + if (read_data != 8'h8B) $display("ERROR: ADD test failed"); + + cpu_read(3'h4, read_data); // Read STATUS + $display("STATUS after ADD: 0x%02h (expected: 0x00, ZERO=0)", read_data); + if (read_data != 8'h00) $display("ERROR: ADD status test failed"); + + // Test 3: CONS Test + $display("\nTest 3: CONS Test"); + cpu_write(3'h1, 8'h41); // ARG1 = ATOM('T') + cpu_write(3'h2, 8'h42); // ARG2 = ATOM('B') + cpu_write(3'h0, 8'h01); // OPCODE = CONS + + cpu_read(3'h3, read_data); // Read RESULT + $display("CONS Result: 0x%02h (expected: 0xC0, CONS(0))", read_data); + if (read_data != 8'hC0) $display("ERROR: CONS test failed"); + + // Test CAR and CDR + cpu_write(3'h1, read_data); // Use the CONS result as ARG1 + cpu_write(3'h0, 8'h02); // OPCODE = CAR + + cpu_read(3'h3, read_data); // Read RESULT + $display("CAR Result: 0x%02h (expected: 0x41, ATOM('T'))", read_data); + if (read_data != 8'h41) $display("ERROR: CAR test failed"); + + cpu_write(3'h1, 8'hC0); // ARG1 = CONS(0) + cpu_write(3'h0, 8'h03); // OPCODE = CDR + + cpu_read(3'h3, read_data); // Read RESULT + $display("CDR Result: 0x%02h (expected: 0x42, ATOM('B'))", read_data); + if (read_data != 8'h42) $display("ERROR: CDR test failed"); + + // Test 4: Error Test (CAR on a Number) + $display("\nTest 4: Error Test (CAR on a Number)"); + cpu_write(3'h1, 8'h85); // ARG1 = NUMBER(5) + cpu_write(3'h0, 8'h02); // OPCODE = CAR + + cpu_read(3'h4, read_data); // Read STATUS + $display("STATUS after CAR on Number: 0x%02h (expected: 0x04, ERR_TYPE=1)", read_data); + if (read_data != 8'h04) $display("ERROR: Error test failed"); + + // Test 5: Heap Full Test + $display("\nTest 5: Heap Full Test"); + // Fill the heap with CONS operations + // Since bump_alloc is now 2, we have used 2 cells. + // 16 cells total. 14 left. 7 more CONS possible. + for (i = 0; i < 7; i = i + 1) begin + cpu_write(3'h1, 8'h41); + cpu_write(3'h2, 8'h42); + cpu_write(3'h0, 8'h01); + cpu_read(3'h3, read_data); + end + + // One more CONS should cause heap full error + cpu_write(3'h1, 8'h41); + cpu_write(3'h2, 8'h42); + cpu_write(3'h0, 8'h01); + + cpu_read(3'h4, read_data); // Read STATUS + $display("STATUS after Heap Full: 0x%02h (expected: 0x02, ERR_HEAP_FULL=1)", read_data); + if (read_data != 8'h02) $display("ERROR: Heap Full test failed"); + + $display("\nAll tests completed."); + $finish; + end + +endmodule -- cgit v1.2.3