diff options
Diffstat (limited to 'rtl/lisp_coproc.sv')
| -rw-r--r-- | rtl/lisp_coproc.sv | 323 |
1 files changed, 135 insertions, 188 deletions
diff --git a/rtl/lisp_coproc.sv b/rtl/lisp_coproc.sv index bd3057a..b855b7b 100644 --- a/rtl/lisp_coproc.sv +++ b/rtl/lisp_coproc.sv | |||
| @@ -8,248 +8,195 @@ module lisp_coproc ( | |||
| 8 | output reg [7:0] data_out | 8 | output reg [7:0] data_out |
| 9 | ); | 9 | ); |
| 10 | 10 | ||
| 11 | // FSM States (One-Hot Encoding) | 11 | // ======================================================================== |
| 12 | parameter [4:0] RESET = 5'b00001; | 12 | // 1. DATAPATH Signals & Storage |
| 13 | parameter [4:0] IDLE = 5'b00010; | 13 | // ======================================================================== |
| 14 | parameter [4:0] DECODE = 5'b00100; | 14 | |
| 15 | parameter [4:0] EXECUTE = 5'b01000; | 15 | // Registers |
| 16 | parameter [4:0] WRITEBACK= 5'b10000; | 16 | reg [7:0] opcode_reg, arg1_reg, arg2_reg, result_reg; |
| 17 | 17 | reg [7:0] heap [0:15]; | |
| 18 | // Internal Registers | 18 | |
| 19 | reg [4:0] state, next_state; | 19 | // Bump Allocator: Uniform 4-bit register (0-15) |
| 20 | reg [7:0] opcode_reg, arg1_reg, arg2_reg, result_reg, status_reg; | 20 | reg [3:0] bump_alloc; |
| 21 | reg [7:0] heap [0:15]; // 16 entries x 8 bits | ||
| 22 | 21 | ||
| 23 | // FIX: Bump allocator must be 5 bits to hold the value '16' (Full) | 22 | // Heap Status: Sticky bit to track if we have wrapped around (Full) |
| 24 | // without wrapping around to 0. | 23 | reg heap_filled; |
| 25 | reg [4:0] bump_alloc; | ||
| 26 | 24 | ||
| 27 | // Status Register Bits | 25 | // Internal Flags (Transient for current OP) |
| 28 | wire busy = (state != IDLE); | 26 | reg flag_err_heap, flag_err_type, flag_carry, flag_zero; |
| 29 | reg err_heap_full, err_type, carry, zero; | ||
| 30 | 27 | ||
| 31 | // ALU Signals | 28 | // ALU Signals |
| 32 | reg [5:0] alu_a, alu_b; | 29 | wire [5:0] alu_val_a = arg1_reg[5:0]; |
| 30 | wire [5:0] alu_val_b = arg2_reg[5:0]; | ||
| 31 | wire [6:0] alu_sum = alu_val_a + alu_val_b; | ||
| 32 | wire alu_eq = (arg1_reg == arg2_reg); | ||
| 33 | |||
| 34 | // Type Checkers | ||
| 35 | wire is_cons_a = (arg1_reg[7:6] == 2'b11); | ||
| 36 | wire is_cons_b = (arg2_reg[7:6] == 2'b11); | ||
| 37 | wire is_num_a = (arg1_reg[7:6] == 2'b10); | ||
| 38 | wire is_num_b = (arg2_reg[7:6] == 2'b10); | ||
| 39 | |||
| 40 | // Allocation Logic (Datapath Adder) | ||
| 41 | // We use a 5-bit wire to capture the carry out. | ||
| 42 | // If bump_alloc is 14 (1110) + 2 = 16 (10000). | ||
| 43 | // alloc_sum[4] (Carry) is 1. alloc_sum[3:0] is 0000. | ||
| 44 | wire [4:0] alloc_sum = {1'b0, bump_alloc} + 5'd2; | ||
| 45 | wire alloc_carry = alloc_sum[4]; | ||
| 46 | |||
| 47 | // ======================================================================== | ||
| 48 | // 2. FSM CONTROLLER | ||
| 49 | // ======================================================================== | ||
| 33 | 50 | ||
| 34 | // Temporary registers for operations | 51 | parameter [4:0] RESET = 5'b00001; |
| 35 | reg [7:0] temp_result; | 52 | parameter [4:0] IDLE = 5'b00010; |
| 36 | reg [3:0] temp_ptr; | 53 | parameter [4:0] DECODE = 5'b00100; |
| 54 | parameter [4:0] EXECUTE = 5'b01000; | ||
| 55 | parameter [4:0] WRITEBACK = 5'b10000; | ||
| 56 | |||
| 57 | reg [4:0] state, next_state; | ||
| 37 | 58 | ||
| 38 | // FSM State Transition | ||
| 39 | always @(posedge clk or posedge rst) begin | 59 | always @(posedge clk or posedge rst) begin |
| 40 | if (rst) begin | 60 | if (rst) state <= RESET; |
| 41 | state <= RESET; | 61 | else state <= next_state; |
| 42 | end else begin | ||
| 43 | state <= next_state; | ||
| 44 | end | ||
| 45 | end | 62 | end |
| 46 | 63 | ||
| 47 | // FSM Combinational Logic | ||
| 48 | always @(*) begin | 64 | always @(*) begin |
| 49 | next_state = state; | 65 | next_state = state; |
| 50 | |||
| 51 | case (state) | 66 | case (state) |
| 52 | RESET: begin | 67 | RESET: next_state = IDLE; |
| 53 | next_state = IDLE; | 68 | IDLE: if (cs && !rw && addr == 3'h0) next_state = DECODE; |
| 54 | end | 69 | DECODE: next_state = EXECUTE; |
| 55 | 70 | EXECUTE: next_state = WRITEBACK; | |
| 56 | IDLE: begin | 71 | WRITEBACK: next_state = IDLE; |
| 57 | if (cs && !rw && addr == 3'h0) begin // Writing to OPCODE triggers operation | 72 | default: next_state = IDLE; |
| 58 | next_state = DECODE; | ||
| 59 | end | ||
| 60 | end | ||
| 61 | |||
| 62 | DECODE: begin | ||
| 63 | next_state = EXECUTE; | ||
| 64 | end | ||
| 65 | |||
| 66 | EXECUTE: begin | ||
| 67 | next_state = WRITEBACK; | ||
| 68 | end | ||
| 69 | |||
| 70 | WRITEBACK: begin | ||
| 71 | next_state = IDLE; | ||
| 72 | end | ||
| 73 | |||
| 74 | default: begin | ||
| 75 | next_state = IDLE; | ||
| 76 | end | ||
| 77 | endcase | 73 | endcase |
| 78 | end | 74 | end |
| 79 | 75 | ||
| 80 | // Register File and Memory Interface | 76 | // ======================================================================== |
| 77 | // 3. SEQUENTIAL LOGIC | ||
| 78 | // ======================================================================== | ||
| 79 | |||
| 80 | integer i; | ||
| 81 | always @(posedge clk or posedge rst) begin | 81 | always @(posedge clk or posedge rst) begin |
| 82 | if (rst) begin | 82 | if (rst) begin |
| 83 | opcode_reg <= 8'h00; | 83 | opcode_reg <= 8'h00; |
| 84 | arg1_reg <= 8'h00; | 84 | arg1_reg <= 8'h00; |
| 85 | arg2_reg <= 8'h00; | 85 | arg2_reg <= 8'h00; |
| 86 | result_reg <= 8'h00; | 86 | result_reg <= 8'h00; |
| 87 | status_reg <= 8'h00; | 87 | bump_alloc <= 4'h0; |
| 88 | bump_alloc <= 5'h00; // Reset 5-bit register | 88 | heap_filled <= 1'b0; |
| 89 | err_heap_full <= 1'b0; | 89 | |
| 90 | err_type <= 1'b0; | 90 | flag_err_heap <= 1'b0; |
| 91 | carry <= 1'b0; | 91 | flag_err_type <= 1'b0; |
| 92 | zero <= 1'b0; | 92 | flag_carry <= 1'b0; |
| 93 | flag_zero <= 1'b0; | ||
| 94 | |||
| 95 | for (i=0; i<16; i=i+1) heap[i] <= 8'h00; | ||
| 96 | |||
| 93 | end else begin | 97 | end else begin |
| 94 | // Memory-mapped register writes | 98 | |
| 99 | // --- MMIO Writes --- | ||
| 95 | if (cs && !rw) begin | 100 | if (cs && !rw) begin |
| 96 | case (addr) | 101 | case (addr) |
| 97 | 3'h0: opcode_reg <= data_in; | 102 | 3'h0: opcode_reg <= data_in; |
| 98 | 3'h1: arg1_reg <= data_in; | 103 | 3'h1: arg1_reg <= data_in; |
| 99 | 3'h2: arg2_reg <= data_in; | 104 | 3'h2: arg2_reg <= data_in; |
| 100 | 3'h3: result_reg <= data_in; // Direct write to result | 105 | 3'h3: result_reg <= data_in; |
| 101 | 3'h4: status_reg <= data_in; // Direct write to status | ||
| 102 | endcase | 106 | endcase |
| 103 | end | 107 | end |
| 104 | 108 | ||
| 105 | // FSM State-specific operations | 109 | // --- State Actions --- |
| 106 | case (state) | 110 | case (state) |
| 107 | RESET: begin | 111 | RESET: begin |
| 108 | // Clear heap on reset | 112 | bump_alloc <= 4'h0; |
| 109 | integer i; | 113 | heap_filled <= 1'b0; |
| 110 | for (i = 0; i < 16; i = i + 1) begin | ||
| 111 | heap[i] <= 8'h00; | ||
| 112 | end | ||
| 113 | bump_alloc <= 5'h00; | ||
| 114 | opcode_reg <= 8'h00; | ||
| 115 | arg1_reg <= 8'h00; | ||
| 116 | arg2_reg <= 8'h00; | ||
| 117 | result_reg <= 8'h00; | ||
| 118 | status_reg <= 8'h00; | ||
| 119 | end | 114 | end |
| 120 | 115 | ||
| 121 | IDLE: begin | 116 | IDLE: begin |
| 122 | // Only clear flags when a NEW operation starts. | ||
| 123 | if (cs && !rw && addr == 3'h0) begin | 117 | if (cs && !rw && addr == 3'h0) begin |
| 124 | err_heap_full <= 1'b0; | 118 | flag_err_heap <= 1'b0; |
| 125 | err_type <= 1'b0; | 119 | flag_err_type <= 1'b0; |
| 126 | carry <= 1'b0; | 120 | flag_carry <= 1'b0; |
| 127 | zero <= 1'b0; | 121 | flag_zero <= 1'b0; |
| 128 | end | 122 | end |
| 129 | end | 123 | end |
| 130 | 124 | ||
| 131 | EXECUTE: begin | 125 | EXECUTE: begin |
| 132 | // Assign ALU inputs for ADD operation using BLOCKING assignment | ||
| 133 | alu_a = arg1_reg[5:0]; | ||
| 134 | alu_b = arg2_reg[5:0]; | ||
| 135 | |||
| 136 | case (opcode_reg) | 126 | case (opcode_reg) |
| 137 | // CONS operation | 127 | 8'h01: begin // CONS |
| 138 | 8'h01: begin | 128 | if (heap_filled) begin |
| 139 | // 5-bit arithmetic: 16 + 2 = 18. 18 > 16 is TRUE. | 129 | // If sticky flag is set, we are full. Error. |
| 140 | if (bump_alloc + 2 > 16) begin | 130 | flag_err_heap <= 1'b1; |
| 141 | err_heap_full <= 1'b1; | ||
| 142 | end else begin | 131 | end else begin |
| 143 | // Store ARG1 and ARG2 in heap | 132 | // Perform allocation |
| 144 | heap[bump_alloc[3:0]] <= arg1_reg; | 133 | heap[bump_alloc] <= arg1_reg; |
| 145 | heap[bump_alloc[3:0] + 1] <= arg2_reg; | 134 | heap[bump_alloc + 1] <= arg2_reg; |
| 146 | // Return CONS tag with pointer as value | 135 | |
| 147 | temp_ptr <= bump_alloc[3:0]; | 136 | // Update pointer (wraps automatically due to 4-bit) |
| 148 | bump_alloc <= bump_alloc + 2; | 137 | bump_alloc <= alloc_sum[3:0]; |
| 138 | |||
| 139 | // If we generated a carry (14->16), mark heap as filled | ||
| 140 | if (alloc_carry) heap_filled <= 1'b1; | ||
| 149 | end | 141 | end |
| 150 | end | 142 | end |
| 151 | 143 | 8'h02: begin // CAR | |
| 152 | // CAR operation | 144 | if (!is_cons_a) flag_err_type <= 1'b1; |
| 153 | 8'h02: begin | ||
| 154 | if (arg1_reg[7:6] != 2'b11) begin // Not a CONS | ||
| 155 | err_type <= 1'b1; | ||
| 156 | end else begin | ||
| 157 | temp_result <= heap[arg1_reg[3:0]]; | ||
| 158 | end | ||
| 159 | end | 145 | end |
| 160 | 146 | 8'h03: begin // CDR | |
| 161 | // CDR operation | 147 | if (!is_cons_a) flag_err_type <= 1'b1; |
| 162 | 8'h03: begin | ||
| 163 | if (arg1_reg[7:6] != 2'b11) begin // Not a CONS | ||
| 164 | err_type <= 1'b1; | ||
| 165 | end else begin | ||
| 166 | temp_result <= heap[arg1_reg[3:0] + 1]; | ||
| 167 | end | ||
| 168 | end | 148 | end |
| 169 | 149 | 8'h05: begin // EQ | |
| 170 | // ATOM operation | 150 | if (alu_eq) flag_zero <= 1'b1; |
| 171 | 8'h04: begin | ||
| 172 | if (arg1_reg[7:6] == 2'b11) begin // Is a CONS | ||
| 173 | temp_result <= 8'h00; // NIL | ||
| 174 | end else begin | ||
| 175 | temp_result <= 8'h41; // 'T' (01_000001) | ||
| 176 | end | ||
| 177 | end | 151 | end |
| 178 | 152 | 8'h06: begin // ADD | |
| 179 | // EQ operation | 153 | if (!is_num_a || !is_num_b) begin |
| 180 | 8'h05: begin | 154 | flag_err_type <= 1'b1; |
| 181 | if (arg1_reg == arg2_reg) begin | ||
| 182 | temp_result <= 8'h41; // 'T' (01_000001) | ||
| 183 | zero <= 1'b1; | ||
| 184 | end else begin | 155 | end else begin |
| 185 | temp_result <= 8'h00; // NIL | 156 | if (alu_sum[6]) flag_carry <= 1'b1; |
| 186 | end | 157 | if (alu_sum[5:0] == 6'd0) flag_zero <= 1'b1; |
| 187 | end | ||
| 188 | |||
| 189 | // ADD operation | ||
| 190 | 8'h06: begin | ||
| 191 | if (arg1_reg[7:6] != 2'b10 || arg2_reg[7:6] != 2'b10) begin // Not both NUMBERs | ||
| 192 | err_type <= 1'b1; | ||
| 193 | end else begin | ||
| 194 | if ((alu_a + alu_b) > 6'd63) begin | ||
| 195 | carry <= 1'b1; | ||
| 196 | end | ||
| 197 | if ((alu_a + alu_b) == 6'd0) begin | ||
| 198 | zero <= 1'b1; | ||
| 199 | end | ||
| 200 | temp_result <= {2'b10, alu_a + alu_b}; // NUMBER tag with sum | ||
| 201 | end | 158 | end |
| 202 | end | 159 | end |
| 203 | endcase | 160 | endcase |
| 204 | end | 161 | end |
| 205 | 162 | ||
| 206 | WRITEBACK: begin | 163 | WRITEBACK: begin |
| 207 | // Write the result based on the operation | ||
| 208 | case (opcode_reg) | 164 | case (opcode_reg) |
| 209 | 8'h01: begin // CONS | 165 | 8'h01: begin // CONS |
| 210 | if (!err_heap_full) begin | 166 | if (!flag_err_heap) |
| 211 | // Tag [7:6] must be set correctly. | 167 | // Math trick: If bump_alloc wrapped to 0, |
| 212 | result_reg <= {2'b11, 2'b00, temp_ptr}; | 168 | // 0 - 2 = 14 (1110 in 2's comp), which is the correct pointer. |
| 213 | end | 169 | result_reg <= {2'b11, 2'b00, bump_alloc - 4'd2}; |
| 214 | end | 170 | end |
| 215 | 171 | 8'h02: begin // CAR | |
| 216 | 8'h02: begin // CAR | 172 | if (!flag_err_type) result_reg <= heap[arg1_reg[3:0]]; |
| 217 | if (!err_type) begin | ||
| 218 | result_reg <= temp_result; | ||
| 219 | end | ||
| 220 | end | 173 | end |
| 221 | 174 | 8'h03: begin // CDR | |
| 222 | 8'h03: begin // CDR | 175 | if (!flag_err_type) result_reg <= heap[arg1_reg[3:0] + 1]; |
| 223 | if (!err_type) begin | ||
| 224 | result_reg <= temp_result; | ||
| 225 | end | ||
| 226 | end | 176 | end |
| 227 | 177 | 8'h04: begin // ATOM | |
| 228 | 8'h04: begin // ATOM | 178 | result_reg <= is_cons_a ? 8'h00 : 8'h41; |
| 229 | result_reg <= temp_result; | ||
| 230 | end | 179 | end |
| 231 | 180 | 8'h05: begin // EQ | |
| 232 | 8'h05: begin // EQ | 181 | result_reg <= alu_eq ? 8'h41 : 8'h00; |
| 233 | result_reg <= temp_result; | ||
| 234 | end | 182 | end |
| 235 | 183 | 8'h06: begin // ADD | |
| 236 | 8'h06: begin // ADD | 184 | if (!flag_err_type) result_reg <= {2'b10, alu_sum[5:0]}; |
| 237 | if (!err_type) begin | ||
| 238 | result_reg <= temp_result; | ||
| 239 | end | ||
| 240 | end | 185 | end |
| 241 | endcase | 186 | endcase |
| 242 | end | 187 | end |
| 243 | endcase | 188 | endcase |
| 244 | end | 189 | end |
| 245 | end | 190 | end |
| 191 | |||
| 192 | // ======================================================================== | ||
| 193 | // 4. OUTPUT LOGIC | ||
| 194 | // ======================================================================== | ||
| 246 | 195 | ||
| 247 | // Update status register | 196 | wire busy_bit = (state != IDLE); |
| 248 | always @(*) begin | 197 | // Note: bit 1 is the transient error flag, not the internal sticky state |
| 249 | status_reg = {3'b000, zero, carry, err_type, err_heap_full, busy}; | 198 | wire [7:0] current_status = {3'b000, flag_zero, flag_carry, flag_err_type, flag_err_heap, busy_bit}; |
| 250 | end | 199 | |
| 251 | |||
| 252 | // Output Logic | ||
| 253 | always @(*) begin | 200 | always @(*) begin |
| 254 | if (cs && rw) begin | 201 | if (cs && rw) begin |
| 255 | case (addr) | 202 | case (addr) |
| @@ -257,11 +204,11 @@ module lisp_coproc ( | |||
| 257 | 3'h1: data_out = arg1_reg; | 204 | 3'h1: data_out = arg1_reg; |
| 258 | 3'h2: data_out = arg2_reg; | 205 | 3'h2: data_out = arg2_reg; |
| 259 | 3'h3: data_out = result_reg; | 206 | 3'h3: data_out = result_reg; |
| 260 | 3'h4: data_out = status_reg; | 207 | 3'h4: data_out = current_status; |
| 261 | default: data_out = 8'h00; | 208 | default: data_out = 8'h00; |
| 262 | endcase | 209 | endcase |
| 263 | end else begin | 210 | end else begin |
| 264 | data_out = 8'hZZ; // High impedance when not reading | 211 | data_out = 8'hZZ; |
| 265 | end | 212 | end |
| 266 | end | 213 | end |
| 267 | 214 | ||
