Skip to content

Instantly share code, notes, and snippets.

@four0four
Created May 16, 2016 21:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save four0four/753ea6761247cc18ef39363738b314d7 to your computer and use it in GitHub Desktop.
Save four0four/753ea6761247cc18ef39363738b314d7 to your computer and use it in GitHub Desktop.
terribad BFCPU
/* 8 bit brainfuck cpu
** Author's note: This is a really bad processor. It does all sorts of stupid/bad things in the aim of brainfucking hardware.
** codeMem -> 3 bit wire, input opcodes from code memory
** codeAddr -> 8 bit address, current pc (usually)
** dataMem -> 8 bit read/write to the working array
** dataAddr -> 8 bit address to working array
** !USE READ FIRST!
*/
// TODO - only exec non-[] opcodes if execEn is set
// add ic inc/dec into [] opcodes
// always inc/dec if execEn is clear, according to skipTo{open,close}
// still check opcodes - if we hit a [], we want to clear skipTo so the next clock resumes normal execution
module bfcpu(
input wire clk, // cpu clock
input wire reset, // active low reset (pull up to run)
input wire [2:0] codeMem, // brainfuck code memory data
output wire [7:0] codeAddr, // brainfuck code memory address
output wire dataWriteEn, // brainfuck data/work array write enable line
input wire [7:0] dataMemIn, // brainfuck data/work array (into CPU)
output wire [7:0] dataMemOut, // brainfuck data/work array (into RAM)
output wire [7:0] dataAddr); // brainfuck data/work address
parameter PINC=3'b000; // '>'
parameter PDEC=3'b001; // '<'
parameter DINC=3'b010; // '+' (two cycle)
parameter DDEC=3'b011; // '-' (two cycle)
parameter OUTB=3'b100; // '.' (??? cycle)
parameter INPB=3'b101; // ',' (do we even implement this?)
parameter JZER=3'b110; // '['
parameter RETZ=3'b111; // ']'
// internal registers
reg [7:0] ic; // instruction counter
reg [7:0] dc; // data counter
reg skipToOpen; // skipping to a '[' (decrementing ic)
reg skipToClose; // skipping to a ']'
reg [7:0] toWrite; // directly written to RAM
reg dataOp; // set to indicate we're in the middle of a data op - these take two cycles (read / writeback)
reg memWrite;
wire execEn = !(skipToOpen || skipToClose) && !(codeAddr == 8'hFF);
wire icDir = skipToClose || !(skipToOpen); // run backward (0) only if skiptoopen is set, otherwise go fwd (1)
wire [7:0] icNext = (icDir ? (ic + 1) : (ic - 1));
always @(posedge clk) begin
// internal wires
if (reset == 1'b1) begin
// run this shit
if (dataOp == 1'b0) begin
memWrite <= 0;
end
case(codeMem)
DINC:
if (dataOp == 1'b1) begin
toWrite <= dataMemIn + 1;
dataOp <= 0;
ic <= icNext;
end else if (execEn == 1'b1) begin // wait for dmem read
dataOp <= 1;
memWrite <= 1;
end
else if (execEn == 1'b0) ic <= icNext;
DDEC:
if (dataOp == 1'b1) begin
toWrite <= dataMemIn - 1;
dataOp <= 0;
ic <= icNext;
end else if (execEn == 1'b1) begin // wait for dmem read
dataOp <= 1;
memWrite <= 1;
end
else if (execEn == 1'b0) ic <= icNext;
PINC:
if (execEn == 1'b1) begin
dc <= dc + 1;
ic <= icNext;
end
else if (execEn == 1'b0) ic <= icNext;
PDEC:
if (execEn == 1'b1) begin
dc <= dc - 1;
ic <= icNext;
end
else if (execEn == 1'b0) ic <= icNext;
JZER:
if (dataOp == 1'b1) begin
dataOp <= 0;
if (dataMemIn == 0) begin // go find a ']', then resume exec
skipToClose <= 1;
ic <= icNext;
end else begin // we're going to be back - don't skip exec
ic <= icNext;
end
end else if (skipToOpen == 1'b1) begin // we were looking for this!
skipToOpen <= 0;
dataOp <= 1; // necessary?
end else if (execEn == 1'b1) begin
dataOp <= 1;
end
RETZ:
if (dataOp == 1'b1) begin
dataOp <= 0;
if(skipToClose == 1'b1) begin
skipToClose <= 0; // found it! resume exec
ic <= ic + 1;
end else begin // we've been executing on the way
skipToOpen <= 1;
ic <= ic - 1;
end
end else begin
dataOp <= 1;
end
default: ic <= icNext; // wtf? just try the next one...
endcase
end
else begin
// startup conditions
ic <= 0;
dc <= 0;
skipToClose <= 0;
skipToOpen <= 0;
end
end
/* // determine next instruction counter combinatorially.
always @(posedge clk) begin
if(icDir == 1'b1) icNext <= ic + 1;
else icNext <= ic - 1;
end*/
assign dataWriteEn = memWrite;
assign dataMemOut = toWrite;
assign dataAddr = dc;
assign codeAddr = ic;
endmodule
module bftop(
input CLK,
input BTN_CLK,
input BTN_RST,
output [7:0] LED);
wire [7:0] dMemAddr;
wire [7:0] dMemIn;
wire [7:0] dMemOut;
wire [7:0] cMemAddr;
wire [2:0] cMemOut;
wire dWriteLine;
wire debugClock;
wire incTest;
reg [7:0] cnt;
BUFG buttonBuf(
.I(incTest),
.O(debugClock) );
debugClock test(
.button(BTN_CLK),
.clk(CLK),
.debugClk(incTest));
bfcpu cpu(
.clk(debugClock),
.reset(~BTN_RST),
.codeMem(cMemOut),
.codeAddr(cMemAddr),
.dataWriteEn(dWriteLine),
.dataMemIn(dMemIn),
.dataMemOut(dMemOut),
.dataAddr(dMemAddr)
);
/* debugDataMem dMem(
.clk(CLK),
.we(dWriteLine),
.dout(dMemIn),
.debugOut(LED),
.din(dMemOut),
.addr(dMemAddr)
);*/
blk_mem_gen_v7_3_0 cMem(
.clka(CLK),
.addra(cMemAddr),
.douta(cMemOut)
);
blk_mem_gen_v7_3_1 dMem(
.clka(CLK),
.addra(dMemAddr),
.dina(dMemOut),
.douta(dMemIn),
.wea(dWriteLine)
);
assign LED = dMemIn;
endmodule
// temporary data memory - used for testing on hardware
module debugDataMem(
input wire clk,
input wire we,
output reg [7:0] dout,
output wire [7:0] debugOut,
input wire [7:0] din,
input wire [7:0] addr);
reg [7:0] data [0:255];
always @ (posedge clk) begin
if(we == 1'b0) begin
data[addr] <= din;
end
dout <= data[addr];
end
assign debugOut = data[7'b0];
endmodule
// test this - should be alright for testing
module debugClock(
input wire button,
input wire clk,
output wire debugClk);
reg [15:0] cnt;
reg [1:0] sync;
reg state;
wire nochange = (sync[0] == ~state);
always @ (posedge clk) begin
sync[0] <= ~button; // pulled down
sync[1] <= sync[0];
end
always @ (posedge clk) begin
if(nochange) begin
cnt <= 0;
end else begin
cnt <= cnt + 1;
if(cnt == 0'hFFFF)
state <= ~state;
end
end
assign debugClk = state;
endmodule
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment