Compare commits

..

2 commits

Author SHA1 Message Date
Bee 2e4cd0240c memory !! 2023-11-21 06:08:22 -05:00
Bee 51ecf47862 heck 2023-11-18 03:03:52 -05:00
18 changed files with 571 additions and 142 deletions

View file

@ -52,8 +52,11 @@ fn main() {
format!("{byte:02X}")
}).collect();
let bit_length = from_contents.len() * 8;
let insert_mem = format!("reg [0:{}] mem = {bit_length}'h{mem_value};", bit_length - 1);
let contents_len = from_contents.len() * 8;
let bit_len = (2048 * 8).max(contents_len * 2);
let padding_len = bit_len - contents_len;
let insert_mem = format!("reg [0:{}] mem = {{{contents_len}'h{mem_value}, {padding_len}'h0}};", bit_len - 1);
into_contents = into_contents.replace("$$insert_mem$$", &insert_mem);

View file

@ -6,14 +6,17 @@ module Beepo #(
) (
input i_clk,
input i_button1,
input i_resume,
output [6:0] o_segments_drive,
output [3:0] o_displays_neg
output [3:0] o_displays_neg,
output o_breakpoint
);
// State values
localparam IDLE = 0; // Start fetching instruction
localparam FETCHI = 1; // Instruction is fetched, start fetching first argument
localparam FETCHA = 2; // Argument byte is fetched
localparam EXEC = 3; // Start running
localparam MEMR = 4; // Transferring bytes between memory and registers
localparam DONE = 7; // Done executing
// Argument types
@ -33,12 +36,10 @@ module Beepo #(
localparam NUM_REGS = 4;
reg [2:0] r_state = IDLE;
reg r_fetching = 0; // counter for waiting before reading from memory
reg [63:0] r_tick = 0;
// Registers
reg [63:0] r_pc = PC_START; // program counter
reg [63:0] r_pc_latch = PC_START; // address input to ROM
reg [63:0] r_registers [0:NUM_REGS]; // up to 255 modifiable registers
reg [7:0] r_instr; // the current instruction
reg [7:0] r_arg_regs [0:3]; // register arguments
@ -52,12 +53,22 @@ module Beepo #(
reg [3:0] r_arg_current_type = 8; // the type of the current argument
reg [5:0] r_arg_bit = 0; // the current lower bit index being fetched for the current argument
reg r_mem_wre = 0;
reg r_mem_busy = 0;
reg r_mem_trans = 0;
reg [7:0] r_mem_in = 0;
reg [7:0] r_mem_index = 0; // the index of the byte in transfer
reg [7:0] r_mem_reg = 0; // the register currently used in transfer
wire [63:0] w_mem_addr = r_mem_trans ? r_arg_addr : r_pc;
wire [7:0] w_mem_fetch;
reg r_breakpoint = 0;
assign o_breakpoint = r_breakpoint;
genvar i;
generate
for (i = 1; i < NUM_REGS-1; i = i + 1) begin
for (i = 0; i <= NUM_REGS; i = i + 1) begin
initial r_registers[i] <= 0;
end
endgenerate
@ -65,18 +76,22 @@ module Beepo #(
always @(posedge i_clk) r_tick <= r_tick + 1;
always @(posedge i_clk) begin
if (r_fetching) r_fetching <= r_fetching + 1;
else case (r_state)
if (r_breakpoint == 1) begin
r_breakpoint = ~i_resume;
end else if (r_mem_busy == 1) begin
r_mem_busy = 0;
end else case (r_state)
IDLE: begin
r_pc_latch <= r_pc;
r_pc <= r_pc + 1;
r_fetching <= 1;
r_state <= FETCHI;
r_mem_wre <= 0;
r_mem_busy <= 1;
end
FETCHI: begin
r_instr <= w_mem_fetch;
r_arg_index <= 0;
r_arg_bit <= 0;
r_mem_trans <= 0;
case (w_mem_fetch)
`TX: r_arg_types_packed = `TX_ARGS;
@ -93,14 +108,17 @@ module Beepo #(
`LI16: r_arg_types_packed = `LI16_ARGS;
`LI32: r_arg_types_packed = `LI32_ARGS;
`LI64: r_arg_types_packed = `LI64_ARGS;
`LD: r_arg_types_packed = `LD_ARGS;
`ST: r_arg_types_packed = `ST_ARGS;
`EBP: r_arg_types_packed = `EBP_ARGS;
default: r_arg_types_packed = {ARG_N, ARG_N, ARG_N, ARG_N};
endcase
r_pc = r_pc + 1;
r_mem_busy = 1;
if (r_arg_types_packed[15:12] != ARG_N) begin
r_state <= FETCHA;
r_pc_latch <= r_pc;
r_pc <= r_pc + 1;
r_fetching <= 1;
r_arg_bytes <= ARG_SIZES[r_arg_types_packed[15:12]*4+:4];
r_arg_current_type <= r_arg_types_packed[15:12];
@ -132,9 +150,8 @@ module Beepo #(
ARG_A: r_arg_addr[r_arg_bit+:8] <= w_mem_fetch;
endcase
r_pc_latch <= r_pc;
r_pc <= r_pc + 1;
r_fetching <= 1;
r_mem_busy <= 1;
r_arg_bytes = r_arg_bytes - 1;
r_arg_bit <= r_arg_bit + 8;
@ -143,21 +160,11 @@ module Beepo #(
r_arg_index = r_arg_index + 1;
r_arg_current_type = r_arg_types[r_arg_index];
if (r_arg_current_type == ARG_N) r_state <= EXEC;
// Execute when there is no next argument or r_arg_index has overflowed
if (r_arg_current_type == ARG_N || r_arg_index == 0) r_state <= EXEC;
else begin
r_arg_bit <= 0;
r_arg_bit <= 0;
r_arg_bytes <= ARG_SIZES[r_arg_current_type*4+:4];
case (r_arg_current_type)
ARG_R: r_arg_regs[r_arg_index] <= 0;
ARG_O: r_arg_addr[r_arg_bit-1] <= 0;
ARG_P: r_arg_addr[r_arg_bit-1] <= 0;
ARG_B: r_arg_imm[r_arg_bit-1] <= 0;
ARG_H: r_arg_imm[r_arg_bit-1] <= 0;
ARG_W: r_arg_imm[r_arg_bit-1] <= 0;
ARG_D: r_arg_imm[r_arg_bit-1] <= 0;
ARG_A: r_arg_addr[r_arg_bit-1] <= 0;
endcase
end
end
end
@ -168,30 +175,109 @@ module Beepo #(
case (r_instr)
`TX: r_state <= DONE;
`NOP: ;
`ADD8: set_register(r_arg_regs[0], r_registers[r_arg_regs[1]] + r_registers[r_arg_regs[2]][7:0]);
`ADD16: set_register(r_arg_regs[0], r_registers[r_arg_regs[1]] + r_registers[r_arg_regs[2]][15:0]);
`ADD32: set_register(r_arg_regs[0], r_registers[r_arg_regs[1]] + r_registers[r_arg_regs[2]][31:0]);
`ADD64: set_register(r_arg_regs[0], r_registers[r_arg_regs[1]] + r_registers[r_arg_regs[2]][63:0]);
`ADDI8: set_register(r_arg_regs[0], r_registers[r_arg_regs[1]] + r_arg_imm[7:0]);
`ADDI16: set_register(r_arg_regs[0], r_registers[r_arg_regs[1]] + r_arg_imm[15:0]);
`ADDI32: set_register(r_arg_regs[0], r_registers[r_arg_regs[1]] + r_arg_imm[31:0]);
`ADDI64: set_register(r_arg_regs[0], r_registers[r_arg_regs[1]] + r_arg_imm[63:0]);
`LI8: set_register(r_arg_regs[0], r_arg_imm);
`LI16: set_register(r_arg_regs[0], r_arg_imm);
`LI32: set_register(r_arg_regs[0], r_arg_imm);
`LI64: set_register(r_arg_regs[0], r_arg_imm);
`ADD8: set_reg_byte (r_arg_regs[0], r_registers[r_arg_regs[1]][0+:8] + r_registers [r_arg_regs[2]][0+:8]);
`ADD16: set_reg_hword (r_arg_regs[0], r_registers[r_arg_regs[1]][0+:16] + r_registers [r_arg_regs[2]][0+:16]);
`ADD32: set_reg_word (r_arg_regs[0], r_registers[r_arg_regs[1]][0+:32] + r_registers [r_arg_regs[2]][0+:32]);
`ADD64: set_reg_dword (r_arg_regs[0], r_registers[r_arg_regs[1]][0+:64] + r_registers [r_arg_regs[2]][0+:64]);
`ADDI8: set_reg_byte (r_arg_regs[0], r_registers[r_arg_regs[1]][0+:8] + r_arg_imm [0+:8]);
`ADDI16: set_reg_hword (r_arg_regs[0], r_registers[r_arg_regs[1]][0+:16] + r_arg_imm [0+:16]);
`ADDI32: set_reg_word (r_arg_regs[0], r_registers[r_arg_regs[1]][0+:32] + r_arg_imm [0+:32]);
`ADDI64: set_reg_dword (r_arg_regs[0], r_registers[r_arg_regs[1]][0+:64] + r_arg_imm [0+:64]);
`LI8: set_reg_byte (r_arg_regs[0], r_arg_imm);
`LI16: set_reg_hword (r_arg_regs[0], r_arg_imm);
`LI32: set_reg_word (r_arg_regs[0], r_arg_imm);
`LI64: set_reg_dword (r_arg_regs[0], r_arg_imm);
`LD: begin
if (r_arg_imm > 0) begin
r_arg_addr <= r_arg_addr + r_registers[r_arg_regs[1]];
r_mem_index <= 0;
r_mem_reg <= r_arg_regs[0];
r_mem_busy <= 1;
r_state <= MEMR;
r_mem_trans <= 1;
end
end
`ST: begin
if (r_arg_imm > 0) begin
r_arg_addr <= r_arg_addr + r_registers[r_arg_regs[1]];
r_mem_index <= 1;
r_mem_reg <= r_arg_regs[0];
r_mem_wre <= 1;
r_mem_in <= r_registers[r_arg_regs[0]][0+:8];
r_mem_busy <= 1;
r_state <= MEMR;
r_mem_trans <= 1;
end
end
`EBP: r_breakpoint = 1;
endcase
end
MEMR: begin
case (r_instr)
`LD: set_reg_part(r_mem_reg, w_mem_fetch, r_mem_index*8);
`ST: r_mem_in <= r_registers[r_mem_reg][r_mem_index*8+:8];
endcase
r_mem_busy <= 1;
if (r_arg_imm == 1) begin
// reached the end of the transfer
r_mem_wre <= 0;
r_mem_trans <= 0;
r_state <= FETCHI;
end else begin
r_mem_index = r_mem_index + 1;
if (r_mem_index == 8) begin
// reached the end of this register
r_mem_reg <= r_mem_reg + 1;
r_mem_index <= 0;
end
r_arg_addr <= r_arg_addr + 1;
r_arg_imm <= r_arg_imm - 1;
end
end
endcase
end
always @(r_registers[0]) r_registers[0] <= 0;
task automatic set_reg_byte(
input [7:0] being_set,
input [7:0] setting_to
);
r_registers[being_set][0+:8] = setting_to;
endtask
task automatic set_register(
task automatic set_reg_hword(
input [7:0] being_set,
input [15:0] setting_to
);
r_registers[being_set][0+:16] = setting_to;
endtask
task automatic set_reg_word(
input [7:0] being_set,
input [31:0] setting_to
);
r_registers[being_set][0+:32] = setting_to;
endtask
task automatic set_reg_dword(
input [7:0] being_set,
input [63:0] setting_to
);
if (being_set != 0) r_registers[being_set] = setting_to;
r_registers[being_set][0+:64] = setting_to;
endtask
task automatic set_reg_part(
input [7:0] being_set,
input [7:0] setting_to,
input [5:0] start_bit
);
if (start_bit <= 56) r_registers[being_set][start_bit+:8] = setting_to;
endtask
Multi7 display (
@ -201,16 +287,14 @@ module Beepo #(
.o_displays_neg(o_displays_neg)
);
// TODO: Bus
// For now this is just ROM
spMem memory (
.dout(w_mem_fetch), //output [7:0] dout
.clk(i_clk), //input clk
.oce(1'b0), //input oce (unused)
.ce(1'b1), //input ce
.reset(1'b0), //input reset
.wre(1'b0), //input wre (write enable)
.ad(r_pc_latch[0+:32]), //input [15:0] ad
.din(1'b0) //input [7:0] din
spMem mem (
.clk(i_clk),
.ad(w_mem_addr),
.din(r_mem_in),
.dout(w_mem_fetch),
.oce(0),
.ce(1),
.reset(0),
.wre(r_mem_wre)
);
endmodule

71
src/bus.v Normal file
View file

@ -0,0 +1,71 @@
// To read:
// 1. Set i_addr to start address
// 2. Set i_flags.0 to 0
// 3. Set i_size to number of bytes to read
// 4. Pulse i_start high
// 5. When o_ready goes high, the read data will be in o_out
// To write:
// 1. Set i_addr to start address
// 2. Set i_in to the data to write
// 3. Set i_flags.0 to 1
// 4. Pulse i_start high
// 5. When o_ready goes high, the transfer is complete
module Bus#(
parameter ADDR_WIDTH = 16,
parameter DATA_WIDTH = 256
) (
input i_clk,
input [ADDR_WIDTH-1:0] i_addr,
input [DATA_WIDTH-1:0] i_in,
input [0:0] i_flags, // flags.0: read(0)/write(1)
input [5:0] i_size,
input i_start, // pulsed high to start transfer
output o_ready,
output [DATA_WIDTH-1:0] o_out
);
localparam F_READ = 'b0;
localparam F_WRITE = 'b1;
localparam S_IDLE = 0;
localparam S_BUSY = 1;
reg r_status = S_IDLE;
reg r_enable = 0;
reg [5:0] r_tx_size = 0;
reg [5:0] r_byte_index = 0;
reg [7:0] r_in = 0;
reg [ADDR_WIDTH-1:0] r_mem_addr;
assign o_ready = r_byte_index == 0 || r_byte_index > r_tx_size;
always @(posedge i_clk or posedge i_start) begin
if (i_start && !r_enable) begin
r_mem_addr <= i_addr;
r_enable <= 1;
r_status <= S_BUSY;
r_tx_size <= i_size;
r_in <= i_in[0+:8];
r_byte_index <= 1; // 0 is transferring now
end else if (r_status == S_BUSY) r_status <= S_IDLE;
else if (r_enable && r_byte_index > r_tx_size) r_enable <= 0;
else if (r_enable) begin
// increment address, input next byte
r_mem_addr <= r_mem_addr + 1;
r_status <= S_BUSY;
r_in <= i_in[r_byte_index*8+:8];
r_byte_index <= r_byte_index + 1;
end
end
spMem memory (
.dout(o_out), //output [7:0] dout
.clk(i_clk), //input clk
.oce(1'b0), //input oce (unused)
.ce(r_enable), //input ce
.reset(1'b0), //input reset
.wre(i_flags[0]), //input wre (write enable)
.ad(r_mem_addr), //input [15:0] ad
.din(r_in) //input [7:0] din
);
endmodule

View file

@ -51,5 +51,15 @@
`define LI64 'h4B
`define LI64_ARGS {ARG_R, ARG_D, ARG_N, ARG_N}
// Absolute addresing memory access operations
`define LD 'h4D
`define LD_ARGS {ARG_R, ARG_R, ARG_A, ARG_H}
`define ST 'h4E
`define ST_ARGS {ARG_R, ARG_R, ARG_A, ARG_H}
// Conditional jump
`define JEQ 'h56
// Environment traps
`define EBP 'h5D
`define EBP_ARGS {ARG_N, ARG_N, ARG_N, ARG_N}

47
tests/Makefile Normal file
View file

@ -0,0 +1,47 @@
SLAPPER_DIR = ../slapper
SLAPPER_BUILD = $(SLAPPER_DIR)/target/release/slapper
SLAPPER = ./slapper
HBASM = ./hbasm
SPMEM = spmem.v
INPUT_FILE = inputs.txt
BUILD_DEPS = ../src/beepo.v ../src/instructions.v ../src/uart_tx.v ../src/multi7.v ../src/bus.v
%.clean: %/build
rm -r $<
$(SLAPPER_BUILD): $(SLAPPER_DIR)/src/main.rs
cargo build --manifest-path $(SLAPPER_DIR)/Cargo.toml -r
$(SLAPPER): $(SLAPPER_BUILD)
cp $< $@
%/build:
mkdir -p $@
%/build/program.bin: %/program.rhai %/build
$(HBASM) $< > $@
%/build/spmem_gen.v: %/build/program.bin $(SLAPPER)
$(SLAPPER) $< $(SPMEM) $@
%/build/out: $(INPUT_FILE) %/$(INPUT_FILE) $(BUILD_DEPS) %/top.v %/build/spmem_gen.v
iverilog -o $@ -c $< -c $(word 2, $^) -s tb_beepo
%/build/dump.vcd: %/build/out
vvp $<
%.wave: %/build/dump.vcd
gtkwave $<
%.assemble: %/build/program.bin
echo Done
%.insert-mem: %/build/spmem_gen.v
echo Done
%.synth: %/build/out
echo Done
%.run: %/build/dump.vcd
echo Done

View file

@ -1,38 +0,0 @@
SLAPPER_DIR = ../../slapper
SLAPPER_BUILD = ${SLAPPER_DIR}/target/release/slapper
SLAPPER = ../slapper
HBASM = ../hbasm
SPMEM = ../spmem.v
INPUT_FILE = inputs.txt
BUILD_DEPS = ../../src/beepo.v ../../src/instructions.v adding.v ../../src/uart_tx.v ../../src/multi7.v build/spmem_gen.v
${SLAPPER_BUILD}:
cargo build --manifest-path ${SLAPPER_DIR}/Cargo.toml -r
${SLAPPER}: ${SLAPPER_BUILD}
cp $< $@
build:
mkdir -p $@
build/program.bin: program.rhai | build
${HBASM} $< > $@
build/spmem_gen.v: build/program.bin ${SLAPPER}
${SLAPPER} $< ${SPMEM} $@
build/out: ${INPUT_FILE} ${BUILD_DEPS} build/spmem_gen.v
iverilog -o $@ -c $< -s tb_adding
build/dump.vcd: build/out
vvp $<
wave: build/dump.vcd
gtkwave build/dump.vcd
assemble: build/program.bin
insert-mem: build/spmem_gen.v
synthesize: build/out
run: build/dump.vcd

View file

@ -1,34 +0,0 @@
`include "../../src/beepo.v"
`timescale 100us/10ns
`define assert(signal, value) \
if (signal !== value) begin \
$display("ASSERTION FAILED in %m: signal != value"); \
$finish; \
end
module tb_adding();
reg clk = 0;
Beepo #(
.FREQ(1),
.UART_BAUD(1_000_000)
) bep (
.i_clk(clk)
);
localparam CLK_PERIOD = 1.0;
always #(CLK_PERIOD/2) clk=~clk;
initial begin
$dumpfile("build/dump.vcd");
$dumpvars(0, tb_adding, bep.r_registers[1]);
end
// should probably do more granular tests
initial #10000 begin
`assert(bep.r_registers[1], 64'h2020202040406090);
$display("[ADDING] All tests passed");
$finish;
end
endmodule

View file

@ -1,5 +1,2 @@
../../src/instructions.v
adding.v
../../src/uart_tx.v
../../src/multi7.v
build/spmem_gen.v
adding/top.v
adding/build/spmem_gen.v

View file

@ -2,15 +2,18 @@ li8 (r1, 0x10);
li16 (r2, 0x1010);
li32 (r3, 0x10101010);
li64 (r4, 0x1010101010101010);
ebp();
add8 (r1, r1, r1);
add16 (r1, r1, r2);
add32 (r1, r1, r3);
add64 (r1, r1, r4);
add8 (r1, r1, r1); // 20
add16 (r1, r1, r2); // 1030
add32 (r1, r1, r3); // 10102040
add64 (r1, r1, r4); // 1010101020203050
ebp();
addi8 (r1, r1, 0x10);
addi16 (r1, r1, 0x1010);
addi32 (r1, r1, 0x10101010);
addi64 (r1, r1, 0x1010101010101010);
addi8 (r1, r1, 0x10); // 1010101020203060
addi16 (r1, r1, 0x1010); // 1010101020204070
addi32 (r1, r1, 0x10101010); // 1010101030305080
addi64 (r1, r1, 0x1010101010101010); // 2020202040406090
ebp();
tx();

73
tests/adding/top.v Normal file
View file

@ -0,0 +1,73 @@
`include "../src/beepo.v"
`timescale 100us/10ns
`define assert(signal, value) \
if (signal !== value) begin \
$display("ASSERTION FAILED in %m: signal != value"); \
$finish; \
end
module tb_beepo();
localparam T_LI = 0;
localparam T_ADD = 1;
localparam T_ADDI = 2;
reg r_clk = 0;
reg r_resume = 0;
reg [1:0] r_test = 0;
wire w_breakpoint;
Beepo #(
.FREQ(1)
) bep (
.i_clk(r_clk),
.i_resume(r_resume),
.o_breakpoint(w_breakpoint)
);
localparam CLK_PERIOD = 1.0;
always #(CLK_PERIOD/2) r_clk=~r_clk;
initial begin
$dumpfile("adding/build/dump.vcd");
$dumpvars(0, tb_beepo,
bep.r_arg_regs[0], bep.r_arg_regs[1],
bep.r_arg_regs[2], bep.r_arg_regs[3],
bep.r_registers[1], bep.r_registers[3]
);
end
// should probably do more granular tests
always @(posedge w_breakpoint) begin
$display("BREAK");
case (r_test)
T_LI: begin
`assert(bep.r_registers[1], 64'h10);
`assert(bep.r_registers[2], 64'h1010);
`assert(bep.r_registers[3], 64'h10101010);
`assert(bep.r_registers[4], 64'h1010101010101010);
$display("[ADDING] LI tests passed");
end
T_ADD: begin
`assert(bep.r_registers[1], 64'h1010101020203050);
$display("[ADDING] ADD test passed");
end
T_ADDI: begin
`assert(bep.r_registers[1], 64'h2020202040406090);
$display("[ADDING] ADDI test passed");
$display("[ADDING] All tests passed");
$finish;
end
endcase
r_test <= r_test + 1;
r_resume = 1;
#2 r_resume = 0;
end
initial #100000 begin
$display("[ADDING] Timeout");
$finish;
end
endmodule

33
tests/gtkw_filters/arg_types.py Executable file
View file

@ -0,0 +1,33 @@
#!/usr/bin/env python3
import sys
arg_types = {
"0": "R",
"1": "O",
"2": "P",
"3": "B",
"4": "H",
"5": "W",
"6": "D",
"7": "A",
"8": "N"
}
def main():
fh_in = sys.stdin
fh_out = sys.stdout
while True:
# incoming values have newline
l = fh_in.readline()
if not l:
return 0
for arg_id in arg_types:
l = l.replace(arg_id, arg_types[arg_id])
fh_out.write(l)
fh_out.flush()
if __name__ == '__main__':
sys.exit(main())

43
tests/gtkw_filters/r_instr.py Executable file
View file

@ -0,0 +1,43 @@
#!/usr/bin/env python3
import sys
instructions = {
0x00: "UN",
0x01: "TX",
0x02: "NOP",
0x03: "ADD8",
0x04: "ADD16",
0x05: "ADD32",
0x06: "ADD64",
0x2D: "ADDI8",
0x2E: "ADDI16",
0x2F: "ADDI32",
0x30: "ADDI64",
0x48: "LI8",
0x49: "LI16",
0x4A: "LI32",
0x4B: "LI64",
0x4D: "LD",
0x4E: "ST",
0x5D: "EBP"
}
def main():
fh_in = sys.stdin
fh_out = sys.stdout
while True:
# incoming values have newline
l = fh_in.readline()
if not l:
return 0
if "x" in l.lower() or "z" in l.lower():
fh_out.write(l)
else:
fh_out.write(f"{instructions.get(int(l, 16))}\n")
fh_out.flush()
if __name__ == '__main__':
sys.exit(main())

36
tests/gtkw_filters/state.py Executable file
View file

@ -0,0 +1,36 @@
#!/usr/bin/env python3
import sys
states = [
"IDLE",
"FETCHI",
"FETCHA",
"EXEC",
"MEMR",
"DONE"
]
def main():
fh_in = sys.stdin
fh_out = sys.stdout
while True:
# incoming values have newline
l = fh_in.readline()
if not l:
return 0
num = int(l, 16)
if "x" in l.lower() or "z" in l.lower():
fh_out.write(l)
else:
if num >= len(states):
fh_out.write(l)
else:
fh_out.write(f"{states[num]}\n")
fh_out.flush()
if __name__ == '__main__':
sys.exit(main())

4
tests/inputs.txt Normal file
View file

@ -0,0 +1,4 @@
../src/instructions.v
../src/uart_tx.v
../src/multi7.v
../src/bus.v

2
tests/mem/inputs.txt Normal file
View file

@ -0,0 +1,2 @@
mem/top.v
mem/build/spmem_gen.v

13
tests/mem/program.rhai Normal file
View file

@ -0,0 +1,13 @@
li64 (r4, 0x1020304050607080);
st (r4, r0, 0x400, 8);
ld (r1, r0, 0x400, 8); // 0x1020304050607080
ebp();
ld (r2, r0, 0x404, 4); // 0x0000000010203040
ebp();
li64 (r1, 0x1010202030304040);
li64 (r2, 0x5050606070708080);
st (r1, r0, 0x410, 16);
ld (r3, r0, 0x410, 16);
ebp();

78
tests/mem/top.v Normal file
View file

@ -0,0 +1,78 @@
`include "../src/beepo.v"
`timescale 100us/10ns
`define assert(signal, value) \
if (signal !== value) begin \
$display("ASSERTION FAILED in %m: signal != value"); \
$finish; \
end
module tb_beepo();
localparam T_STLD = 0;
localparam T_STLD_HALF = 1;
localparam T_STLD_DOUBLE = 2;
localparam T_TESTS = 3;
reg r_clk = 0;
reg r_resume = 0;
reg [1:0] r_test = 0;
wire w_breakpoint;
Beepo #(
.FREQ(1)
) bep (
.i_clk(r_clk),
.i_resume(r_resume),
.o_breakpoint(w_breakpoint)
);
localparam CLK_PERIOD = 1.0;
always #(CLK_PERIOD/2) r_clk=~r_clk;
initial begin
$dumpfile("mem/build/dump.vcd");
$dumpvars(0, tb_beepo,
bep.r_arg_regs[0], bep.r_arg_regs[1],
bep.r_arg_regs[2], bep.r_arg_regs[3],
bep.r_registers[1], bep.r_registers[2],
bep.r_registers[3], bep.r_registers[4]
);
end
// should probably do more granular tests
always @(posedge w_breakpoint) begin
$display("BREAK");
case (r_test)
T_STLD: begin
`assert(bep.r_registers[1], 64'h1020304050607080);
$display("[MEM] ST/LD test passed");
end
T_STLD_HALF: begin
`assert(bep.r_registers[2], 64'h0000000010203040);
$display("[MEM] ST/LD Half test passed");
end
T_STLD_DOUBLE: begin
`assert(bep.r_registers[3], 64'h1010202030304040);
`assert(bep.r_registers[4], 64'h5050606070708080);
$display("[MEM] ST/LD Double test passed");
end
endcase
r_test = r_test + 1;
if (r_test == T_TESTS) begin
$display("[MEM] All tests passed");
$finish;
end
r_resume = 1;
#2 r_resume = 0;
end
initial #100000 begin
$display("[ADDING] Timeout");
$finish;
end
endmodule

View file

@ -18,7 +18,11 @@ module spMem(
always @(negedge clk) begin
// one full clock cycle before being fetched
if (r_ad_prev == ad) r_out <= mem[ad*8+:8];
if (r_ad_prev == ad) begin
if (wre == 1) mem[ad*8+:8] = din;
r_out <= mem[ad*8+:8];
end
else r_ad_prev = ad;
end
endmodule