`timescale 1ns / 1ps
module brainfuck_cpu(
input reset,
input clk,
output [7:0] address_bus,
inout [7:0] data_bus,
output data_write,
output code_mem_en,
output data_mem_en,
output io_mem_en,
output error
);
// Registers
reg [7:0] rip;
reg [7:0] rdp;
reg [7:0] rnl;
reg [7:0] icache;
reg [7:0] dcache;
// Instruction decoding
wire i_inc_dp, i_dec_dp, i_inc_data, i_dec_data;
wire i_out, i_in;
wire i_forward, i_backward;
assign i_inc_dp = (icache == 8'h3e); // >
assign i_dec_dp = (icache == 8'h3c); // <
assign i_inc_data = (icache == 8'h2b); // +
assign i_dec_data = (icache == 8'h2d); // -
assign i_out = (icache == 8'h2e); // .
assign i_in = (icache == 8'h2c); // ,
assign i_forward = (icache == 8'h5b); // [
assign i_backward = (icache == 8'h5d); // ]
// Bus access
reg [2:0] access_latch;
parameter ACCESS_NONE = 4'b000;
parameter ACCESS_CODE = 4'b100;
parameter ACCESS_DATA = 4'b010;
parameter ACCESS_IO = 4'b001;
assign { code_mem_en, data_mem_en, io_mem_en } = (!reset) ? access_latch : 3'bZZZ;
reg [7:0] address_latch;
assign address_bus = (!reset) ? address_latch : 8'hZZ;
reg write_enable;
wire [7:0] data_in = data_bus;
assign data_bus = (!reset && write_enable) ? dcache : 8'hZZ;
assign data_write = (!reset) ? write_enable : 1'bZ;
// FSM definitions
reg [4:0] state;
parameter FETCH = 0;
parameter DECODE = 1;
parameter EXECUTE1 = 2;
parameter EXECUTE2 = 3;
parameter EXECUTE3 = 4;
parameter EXECUTE4 = 5;
parameter SEARCH1 = 6;
parameter SEARCH2 = 7;
parameter SEARCH3 = 8;
parameter INVALID = 9;
reg search_dir;
parameter FORWARD = 1'b0;
parameter BACKWARD = 1'b1;
assign error = (state == INVALID);
// Finite state machine
always @ (posedge clk) begin
if (reset == 1'b1) begin
rip <= 8'b0;
rdp <= 8'b0;
rnl <= 8'b0;
access_latch <= 3'b0;
address_latch <= 8'b0;
write_enable <= 1'b0;
icache <= 8'b0;
dcache <= 8'b0;
state <= FETCH;
end else begin
case (state)
FETCH: begin
access_latch <= ACCESS_CODE;
address_latch <= rip;
state <= DECODE;
end
DECODE: begin
icache <= data_in;
rip <= rip + 1;
state <= EXECUTE1;
end
EXECUTE1: begin
if (i_inc_dp || i_dec_dp) begin
if (i_inc_dp) rdp <= rdp + 1;
else if (i_dec_dp) rdp <= rdp - 1;
access_latch <= ACCESS_NONE;
state <= FETCH;
end else if (i_inc_data || i_dec_data || i_out || i_forward || i_backward) begin
access_latch <= ACCESS_DATA;
address_latch <= rdp;
state <= EXECUTE2;
end else if (i_in) begin
access_latch <= ACCESS_IO;
address_latch <= 8'b0;
state <= EXECUTE2;
end else begin
access_latch <= ACCESS_NONE;
state <= INVALID;
end
end
EXECUTE2: begin
dcache <= data_in;
if (i_backward)
rip <= rip - 1;
state <= EXECUTE3;
end
EXECUTE3: begin
if (i_inc_data || i_dec_data) begin
if(i_inc_data) dcache <= dcache + 1;
else if(i_dec_data) dcache <= dcache - 1;
access_latch <= ACCESS_DATA;
write_enable <= 1'b1;
// address_latch still contains the address from EXECUTE1,
// and dcache contains the updated data
state <= EXECUTE4;
end else if (i_out) begin
access_latch <= ACCESS_IO;
write_enable <= 1'b1;
address_latch <= 8'b0;
state <= EXECUTE4;
end else if (i_in) begin
access_latch <= ACCESS_DATA;
write_enable <= 1'b1;
address_latch <= rdp;
// dcache contains the requested data
state <= EXECUTE4;
end else if (i_forward || i_backward) begin
access_latch <= ACCESS_NONE;
search_dir <= (i_forward) ? FORWARD : BACKWARD;
if ((dcache == 8'b0 && i_forward) ||
(dcache != 8'b0 && i_backward)) begin
if (i_backward)
rip <= rip - 1;
state <= SEARCH1;
end else begin
if (i_backward)
rip <= rip + 1;
state <= FETCH;
end
end
end
EXECUTE4: begin
state <= FETCH;
/* IO memory space is not tolerable to additional clock
* cycles. */
if (io_mem_en) begin
access_latch <= ACCESS_NONE;
write_enable <= 1'b0;
end
end
SEARCH1: begin
access_latch <= ACCESS_CODE;
address_latch <= rip;
state <= SEARCH2;
end
SEARCH2: begin
icache <= data_in;
state <= SEARCH3;
end
SEARCH3: begin
if (search_dir == FORWARD) begin
rip <= rip + 1;
if (i_forward) begin
rnl <= rnl + 1;
state <= SEARCH1;
end else if (i_backward) begin
if (rnl != 0) begin
rnl <= rnl - 1;
state <= SEARCH1;
end else /* rnl = 0 */ begin
state <= FETCH;
end
end else /* !i_forward && !i_backward */ begin
state <= SEARCH1;
end
end else begin
if (i_backward) begin
rnl <= rnl + 1;
rip <= rip - 1;
state <= SEARCH1;
end else if (i_forward) begin
if (rnl != 0) begin
rnl <= rnl - 1;
rip <= rip - 1;
state <= SEARCH1;
end else /* rnl = 0 */ begin
rip <= rip + 1;
state <= FETCH;
end
end else /* !i_forward && !i_backward */ begin
rip <= rip - 1;
state <= SEARCH1;
end
end
end
endcase
end
end
always @(negedge clk) begin
if (state == EXECUTE1 || state == EXECUTE3 ||
state == FETCH || state == SEARCH3) begin
access_latch <= ACCESS_NONE;
write_enable <= 1'b0;
end
end
endmodule