1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
|
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
);
// ========================================================================
// 1. DATAPATH Signals & Storage
// ========================================================================
// Registers
reg [7:0] opcode_reg, arg1_reg, arg2_reg, result_reg;
reg [7:0] heap [0:15];
// Bump Allocator: Uniform 4-bit register (0-15)
reg [3:0] bump_alloc;
// Heap Status: Sticky bit to track if we have wrapped around (Full)
reg heap_filled;
// Internal Flags (Transient for current OP)
reg flag_err_heap, flag_err_type, flag_carry, flag_zero;
// ALU Signals
wire [5:0] alu_val_a = arg1_reg[5:0];
wire [5:0] alu_val_b = arg2_reg[5:0];
wire [6:0] alu_sum = alu_val_a + alu_val_b;
wire alu_eq = (arg1_reg == arg2_reg);
// Type Checkers
wire is_cons_a = (arg1_reg[7:6] == 2'b11);
wire is_cons_b = (arg2_reg[7:6] == 2'b11);
wire is_num_a = (arg1_reg[7:6] == 2'b10);
wire is_num_b = (arg2_reg[7:6] == 2'b10);
// Allocation Logic (Datapath Adder)
// We use a 5-bit wire to capture the carry out.
// If bump_alloc is 14 (1110) + 2 = 16 (10000).
// alloc_sum[4] (Carry) is 1. alloc_sum[3:0] is 0000.
wire [4:0] alloc_sum = {1'b0, bump_alloc} + 5'd2;
wire alloc_carry = alloc_sum[4];
// ========================================================================
// 2. FSM CONTROLLER
// ========================================================================
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;
reg [4:0] state, next_state;
always @(posedge clk or posedge rst) begin
if (rst) state <= RESET;
else state <= next_state;
end
always @(*) begin
next_state = state;
case (state)
RESET: next_state = IDLE;
IDLE: if (cs && !rw && addr == 3'h0) next_state = DECODE;
DECODE: next_state = EXECUTE;
EXECUTE: next_state = WRITEBACK;
WRITEBACK: next_state = IDLE;
default: next_state = IDLE;
endcase
end
// ========================================================================
// 3. SEQUENTIAL LOGIC
// ========================================================================
integer i;
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;
bump_alloc <= 4'h0;
heap_filled <= 1'b0;
flag_err_heap <= 1'b0;
flag_err_type <= 1'b0;
flag_carry <= 1'b0;
flag_zero <= 1'b0;
for (i=0; i<16; i=i+1) heap[i] <= 8'h00;
end else begin
// --- MMIO 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;
endcase
end
// --- State Actions ---
case (state)
RESET: begin
bump_alloc <= 4'h0;
heap_filled <= 1'b0;
end
IDLE: begin
if (cs && !rw && addr == 3'h0) begin
flag_err_heap <= 1'b0;
flag_err_type <= 1'b0;
flag_carry <= 1'b0;
flag_zero <= 1'b0;
end
end
EXECUTE: begin
case (opcode_reg)
8'h01: begin // CONS
if (heap_filled) begin
// If sticky flag is set, we are full. Error.
flag_err_heap <= 1'b1;
end else begin
// Perform allocation
heap[bump_alloc] <= arg1_reg;
heap[bump_alloc + 1] <= arg2_reg;
// Update pointer (wraps automatically due to 4-bit)
bump_alloc <= alloc_sum[3:0];
// If we generated a carry (14->16), mark heap as filled
if (alloc_carry) heap_filled <= 1'b1;
end
end
8'h02: begin // CAR
if (!is_cons_a) flag_err_type <= 1'b1;
end
8'h03: begin // CDR
if (!is_cons_a) flag_err_type <= 1'b1;
end
8'h05: begin // EQ
if (alu_eq) flag_zero <= 1'b1;
end
8'h06: begin // ADD
if (!is_num_a || !is_num_b) begin
flag_err_type <= 1'b1;
end else begin
if (alu_sum[6]) flag_carry <= 1'b1;
if (alu_sum[5:0] == 6'd0) flag_zero <= 1'b1;
end
end
endcase
end
WRITEBACK: begin
case (opcode_reg)
8'h01: begin // CONS
if (!flag_err_heap)
// Math trick: If bump_alloc wrapped to 0,
// 0 - 2 = 14 (1110 in 2's comp), which is the correct pointer.
result_reg <= {2'b11, 2'b00, bump_alloc - 4'd2};
end
8'h02: begin // CAR
if (!flag_err_type) result_reg <= heap[arg1_reg[3:0]];
end
8'h03: begin // CDR
if (!flag_err_type) result_reg <= heap[arg1_reg[3:0] + 1];
end
8'h04: begin // ATOM
result_reg <= is_cons_a ? 8'h00 : 8'h41;
end
8'h05: begin // EQ
result_reg <= alu_eq ? 8'h41 : 8'h00;
end
8'h06: begin // ADD
if (!flag_err_type) result_reg <= {2'b10, alu_sum[5:0]};
end
endcase
end
endcase
end
end
// ========================================================================
// 4. OUTPUT LOGIC
// ========================================================================
wire busy_bit = (state != IDLE);
// Note: bit 1 is the transient error flag, not the internal sticky state
wire [7:0] current_status = {3'b000, flag_zero, flag_carry, flag_err_type, flag_err_heap, busy_bit};
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 = current_status;
default: data_out = 8'h00;
endcase
end else begin
data_out = 8'hZZ;
end
end
endmodule
|