diff --git a/.gitignore b/.gitignore index 1138ab5..8eb0af9 100644 --- a/.gitignore +++ b/.gitignore @@ -6,11 +6,12 @@ *.swp *.memdump *.lxt # Not sure when those crop up -boot_code/brainfuck.bin -boot_code/brainfuck.txt +boot_code/brainfuck_interpreted.bin +boot_code/brainfuck_interpreted.txt boot_code/brainfuck_mandelbrot.bin boot_code/brainfuck_mandelbrot.txt boot_code/brainfuck_compiled.bin boot_code/brainfuck_compiled.txt system/boot_code.bin system/boot_code.txt +system/obj_dir/ diff --git a/Makefile b/Makefile index aed1553..3c9059d 100644 --- a/Makefile +++ b/Makefile @@ -17,6 +17,7 @@ # SYSTEM_VVP=system/system.vvp +VERILATOR_BIN=system/obj_dir/Vsystem BOOT_CODE=boot_code/brainfuck_mandelbrot.txt GTKWSAVE=./gtkwave_savefile.gtkw MICROCODE=system/ucode.txt @@ -38,6 +39,10 @@ boot_code/%.txt: ${SYSTEM_VVP}: ${Q}make ${MAKEOPTS} -C system system.vvp +.PHONY:${VERILATOR_BIN} +${VERILATOR_BIN}: + ${Q}make ${MAKEOPTS} -C system obj_dir/Vsystem + .PHONY: clean clean: ${Q}make ${MAKEOPTS} -C system clean diff --git a/common.mk b/common.mk index 80da53d..89d3a33 100644 --- a/common.mk +++ b/common.mk @@ -1,11 +1,22 @@ .PRECIOUS:${BOOT_CODE} + QUIET=1 +# QUIET: 1=clean, non-verbose output +# 2=normal make output + +SIM=VERILATOR +#SIM=ICARUS +# SIM: VERILATOR: use Verilator +# ICARUS: use Icarus Verilog + ifeq "${QUIET}" "1" - QUIET_AS = @echo ' AS '$@; - QUIET_VVP = @echo ' VVP '$@; - QUIET_IVERILOG = @echo ' IVERILOG '$@; - QUIET_CLEAN = @printf ' CLEAN %s\n' $1; + QUIET_AS = @echo ' AS '$@; + QUIET_VVP = @echo ' VVP '$@; + QUIET_IVERILOG = @echo ' IVERILOG '$@; + QUIET_VERILATOR = @echo ' VERILATOR '$@; + QUIET_CLEAN = @printf ' CLEAN %s\n' $1; + QUIET_VERILATOR_RUN = @printf ' %s %s\n' $1 $2; Q = @ MAKEOPTS=--no-print-directory .SILENT: @@ -34,15 +45,27 @@ ifeq "${NO_ASM}" "0" endif # Running simulation -%.lx2 %.memdump: %.txt ${SYSTEM_VVP} +ifeq "${SIM}" "ICARUS" +%.lx2 %.memdump: %.txt ${SYSTEM_VVP} ${MICROCODE} ${QUIET_VVP} ${Q}vvp "${SYSTEM_VVP}" -lxt2 +BOOT_CODE="$<" +WAVEFORM="$(subst .txt,.lx2,$<)" +MEMDUMP="$(subst .txt,.memdumptxt,$<)" +MICROCODE="${MICROCODE}" ${Q}grep -v '^//' "$(subst .txt,.memdumptxt,$<)" | xxd -ps -c 2 -r > "$(subst .txt,.memdump,$<)" ${Q}rm "$(subst .txt,.memdumptxt,$<)" -%.run: %.txt ${SYSTEM_VVP} +%.run: %.txt ${SYSTEM_VVP} ${MICROCODE} ${QUIET_VVP} ${Q}vvp -i "${SYSTEM_VVP}" +BOOT_CODE="$<" +MICROCODE="${MICROCODE}" +else ifeq "${SIM}" "VERILATOR" +%.lx2 %.memdump: %.txt ${VERILATOR_BIN} ${MICROCODE} + $(call QUIET_VERILATOR_RUN,$(word 2,$^),$<) + ${Q}"${VERILATOR_BIN}" +BOOT_CODE="$<" +WAVEFORM="$(subst .txt,.lx2,$<)" +MEMDUMP="$(subst .txt,.memdumptxt,$<)" +MICROCODE="${MICROCODE}" + ${Q}grep -v '^//' "$(subst .txt,.memdumptxt,$<)" | xxd -ps -c 2 -r > "$(subst .txt,.memdump,$<)" + ${Q}rm "$(subst .txt,.memdumptxt,$<)" + +%.run: %.txt ${VERILATOR_BIN} ${MICROCODE} + $(call QUIET_VERILATOR_RUN,$(word 2,$^),$<) + ${Q}numactl -m 0 -C 17,18 -- "${VERILATOR_BIN}" +BOOT_CODE="$<" +MICROCODE="${MICROCODE}" +endif %.disas: %.bin objdump -D -b binary -m i8086 $^ | less diff --git a/system/Makefile b/system/Makefile index bad380c..d0c9e64 100644 --- a/system/Makefile +++ b/system/Makefile @@ -15,9 +15,13 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -SOURCES=processor.v testbench.v memory.v registers.v alu.v decoder.v general.v -INCLUDES=proc_state_def.v alu_header.v config.v ucode_header.v ${MICROCODE} +TOP_LEVEL_SOURCE=system.v +SOURCES=processor.v memory.v registers.v alu.v decoder.v general.v +EVENT_SIM_TESTBENCH=testbench.v +VERILATOR_TESTBENCH=testbench.cpp +INCLUDES=proc_state_def.v alu_header.v config.v ucode_header.v SYSTEM_VVP=system.vvp +VERILATOR_BIN=obj_dir/Vsystem BOOT_CODE=boot_code.txt GTKWSAVE=../gtkwave_savefile.gtkw MICROCODE=ucode.txt @@ -26,12 +30,15 @@ NO_ASM=0 include ../common.mk # COMPILING -${SYSTEM_VVP} : ${SOURCES} ${INCLUDES} +${SYSTEM_VVP} : ${TOP_LEVEL_SOURCE} ${SOURCES} ${INCLUDES} ${EVENT_SIM_TESTBENCH} ${QUIET_IVERILOG} - ${Q}iverilog -g2012 -o "$@" ${SOURCES} + ${Q}iverilog -g2012 -o "$@" ${TOP_LEVEL_SOURCE} ${SOURCES} ${EVENT_SIM_TESTBENCH} +${VERILATOR_BIN}: ${VERILATOR_TESTBENCH} ${TOP_LEVEL_SOURCE} ${SOURCES} ${INCLUDES} + ${QUIET_VERILATOR} + ${Q}verilator --cc --exe --build -j 0 $^ -Wall --Wno-DECLFILENAME -Wno-SYNCASYNCNET -Wno-MULTIDRIVEN 2>&1 --threads 1 --autoflush >/dev/null #TODO: remove the silencing and make the generated Makefiles print quietly as well .PHONY: clean clean: $(call QUIET_CLEAN,system) - ${Q}rm -f ${SYSTEM_VVP} *.lx2 boot_code.txt boot_code.bin *memdump *memdumptxt + ${Q}rm -rf ${SYSTEM_VVP} *.lx2 boot_code.txt boot_code.bin *memdump *memdumptxt obj_dir diff --git a/system/memory.v b/system/memory.v index 56d4d8e..8c41203 100644 --- a/system/memory.v +++ b/system/memory.v @@ -17,8 +17,13 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ +// don't ask for the full 1MiB especially since we don't even have segmentation +/* verilator lint_off UNUSEDSIGNAL */ module doublemem(input [19:0] address,inout wire [15:0] data ,input rd,input wr,input BHE,input cs); +/* verilator lint_on UNUSEDSIGNAL */ + reg [15:0] memory [0:32768]; + initial begin string boot_code; if(!$value$plusargs("BOOT_CODE=%s",boot_code))begin @@ -28,15 +33,15 @@ initial begin $readmemh(boot_code, memory,0,16383); end -assign data[7:0] = !address[0:0] & !rd & !cs ? memory[address[15:1]][15:8] : 16'hz; +assign data[7:0] = !address[0:0] & !rd & !cs ? memory[address[16:1]][15:8] : 8'hz; -assign data[15:8] = !BHE & !rd & !cs ? memory[address[15:1]][7:0] : 16'hz; +assign data[15:8] = !BHE & !rd & !cs ? memory[address[16:1]][7:0] : 8'hz; always @(negedge wr) begin if(BHE==0) - memory[address[15:1]][7:0]=data[15:8]; + memory[address[16:1]][7:0]<=data[15:8]; if(address[0]==0) - memory[address[15:1]][15:8]=data[7:0]; + memory[address[16:1]][15:8]<=data[7:0]; end endmodule diff --git a/system/system.v b/system/system.v new file mode 100644 index 0000000..ca74ad2 --- /dev/null +++ b/system/system.v @@ -0,0 +1,17 @@ +`define CPU_SPEED 1000 + +module system ( input clock,input reset, output [19:0]address_bus, inout [15:0]data_bus,output BHE, output rd, output wr, output IOMEM, output HALT, output ERROR); +processor p(clock,reset,address_bus,data_bus,rd,wr,BHE,IOMEM,HALT,ERROR); +doublemem sysmem(address_bus,data_bus,rd,wr,BHE,IOMEM); + +string memdump_name; +always @(posedge HALT) begin +// $display("Processor halted.\nCycles run for: %d",cycles); + if($value$plusargs("MEMDUMP=%s",memdump_name))begin + $writememh(memdump_name, sysmem.memory,0,32767); + end +// #(`CPU_SPEED) //Just for the waveform + $finish; +end + +endmodule diff --git a/system/testbench.cpp b/system/testbench.cpp new file mode 100644 index 0000000..e395a7c --- /dev/null +++ b/system/testbench.cpp @@ -0,0 +1,33 @@ +#include "Vsystem.h" +#include "verilated.h" +#include "stdio.h" + +Vsystem *system_state; + +void tick() { + system_state->clock = 1; + system_state->eval(); + system_state->clock = 0; + system_state->eval(); + //printf("tick() %04x\n",system_state->address_bus); + //trace->dump(timestamp); + //timestamp += 500/MHz; +} + +int main(int argc, char** argv) { + VerilatedContext* contextp = new VerilatedContext; + contextp->commandArgs(argc, argv); + system_state = new Vsystem{contextp}; + system_state->reset=1; + tick(); + system_state->reset=0; + tick(); + system_state->reset=1; + while(!contextp->gotFinish()){ + tick(); + } + delete system_state; + delete contextp; + return 0; +} + diff --git a/system/testbench.v b/system/testbench.v index 1352180..37a77ac 100644 --- a/system/testbench.v +++ b/system/testbench.v @@ -25,18 +25,25 @@ reg reset; reg clk_enable; wire [19:0]address_bus; wire [15:0]data_bus; -wire rd,wr,romcs,HALT; +wire rd,wr,HALT; wire ERROR; wire IOMEM; -processor p(clock,reset,address_bus,data_bus,rd,wr,BHE,IOMEM,HALT,ERROR); -doublemem sysmem(address_bus,data_bus,rd,wr,BHE,IOMEM); +system system( .clock(clock), + .reset(reset), + .address_bus(address_bus), + .data_bus(data_bus), + .rd(rd), + .wr(wr), + .HALT(HALT), + .ERROR(ERROR), + .IOMEM(IOMEM) +); `define CPU_SPEED 1000 clock_gen #(.FREQ(1000)) u1(clk_enable, clock); -assign romcs=0; integer cycles=0; string memdump_name; @@ -44,7 +51,7 @@ initial begin string waveform_name; if($value$plusargs("WAVEFORM=%s",waveform_name))begin $dumpfile(waveform_name); - $dumpvars(0,p,u1); + $dumpvars(0,system); end clk_enable = 1; @@ -56,20 +63,12 @@ initial begin reset = 1; end -always @(posedge HALT) begin - $display("Processor halted.\nCycles run for: %d",cycles); - if($value$plusargs("MEMDUMP=%s",memdump_name))begin - $writememh(memdump_name, sysmem.memory,0,32767); - end - #(`CPU_SPEED) //Just for the waveform - $finish; -end always @(posedge ERROR) begin clk_enable <= 0; $display("PROCESSOR RUN INTO AN ERROR.\nCycles run for: %d",cycles); if($value$plusargs("MEMDUMP=%s",memdump_name))begin - $writememh(memdump_name, sysmem.memory,0,32767); + $writememh(memdump_name, system.sysmem.memory,0,32767); end #(`CPU_SPEED) //Just for the waveform $finish; diff --git a/system/ucode.txt b/system/ucode.txt index 40ae329..d5e1cdc 100644 --- a/system/ucode.txt +++ b/system/ucode.txt @@ -55,31 +55,31 @@ // //Nxt M: Next microcode address -@000 0000_000_000__00__00_0000__00_000000 +@000 _00_000_0000_000_0000_000_000__00__00_0000__00_000000 // 36 34 32 28 25 21 18 15 13 11 7 5 0 // CALL // wbo|krs|rr2 |imd|rr1 |a1f|a1o|a12|a11|rwa |nxs|Nxt M | @001 _00_000_0000_011_1100_001_011__00__01_1100__01_000010 // ALU_1: SP ALU_2: PARAM2 (2) ALU_OP:SUB ALU_out: SP (also fetch the opcode argument to PARAM1) -@002 _00_000_0000_011_zzzz_000_110__10__11_zzzz__00_000011 // ALU_1: 0 ALU_2: PC ALU_OP:ADD ALU_out: [SP] -@003 _00_000_0000_011_zzzz_000_101__10__00_zzzz__00_000000 // ALU_1: PARAM1 (arg) ALU_2: PC ALU_OP:ADD ALU_out: PC +@002 _00_000_0000_011_xxxx_000_110__10__11_xxxx__00_000011 // ALU_1: 0 ALU_2: PC ALU_OP:ADD ALU_out: [SP] +@003 _00_000_0000_011_xxxx_000_101__10__00_xxxx__00_000000 // ALU_1: PARAM1 (arg) ALU_2: PC ALU_OP:ADD ALU_out: PC // RET // wbo|krs|rr2 |imd|rr1 |a1f|a1o|a12|a11|rwa |nxs|Nxt M | -@004 _00_000_0000_110_zzzz_000_101__00__11_zzzz__11_000101 // ALU_1: 0 ALU_2: PARAM2 ([SP]) ALU_OP:ADD ALU_out: PC (also read [SP] to PARAM2) +@004 _00_000_0000_110_xxxx_000_101__00__11_xxxx__11_000101 // ALU_1: 0 ALU_2: PARAM2 ([SP]) ALU_OP:ADD ALU_out: PC (also read [SP] to PARAM2) @005 _00_000_1100_011_0000_000_011__01__00_1100__00_000000 // ALU_1: PARAM1 (2) ALU_2: SP ALU_OP:ADD ALU_out: SP // STOS // wbo|krs|rr2 |imd|rr1 |a1f|a1o|a12|a11|rwa |nxs|Nxt M | -@006 _00_000_1000_011_zzzz_000_000__01__11_zzzz__00_000111 // ALU_1: 0 ALU_2: AX ALU_OP:ADD ALU_out: [DI] -@007 _11_000_zzzz_011_1111_000_011__00__01_1111__00_000000 // ALU_1: DI ALU_2: PARAM2 (2) ALU_OP:ADD ALU_OUT: DI +@006 _00_000_1000_011_xxxx_000_000__01__11_xxxx__00_000111 // ALU_1: 0 ALU_2: AX ALU_OP:ADD ALU_out: [DI] +@007 _11_000_xxxx_011_1111_000_011__00__01_1111__00_000000 // ALU_1: DI ALU_2: PARAM2 (2) ALU_OP:ADD ALU_OUT: DI // PUSH // wbo|krs|rr2 |imd|rr1 |a1f|a1o|a12|a11|rwa |nxs|Nxt M | @008 _00_010_0000_011_1100_001_011__00__01_1100__00_001001 // ALU_1: SP ALU_2: PARAM2 (2) ALU_OP:SUB ALU_out: SP (also fetch the opcode argument to PARAM1) -@009 _00_010_0000_011_zzzz_000_110__01__11_zzzz__00_000000 // ALU_1: 0 ALU_2: REG ALU_OP:ADD ALU_out: [SP] +@009 _00_010_0000_011_xxxx_000_110__01__11_xxxx__00_000000 // ALU_1: 0 ALU_2: REG ALU_OP:ADD ALU_out: [SP] // POP // wbo|krs|rr2 |imd|rr1 |a1f|a1o|a12|a11|rwa |nxs|Nxt M | -@00a _00_100_0000_110_zzzz_011_011__00__11_zzzz__11_001011 // ALU_1: 0 ALU_2: PARAM2 ([SP]) ALU_OP:ADD ALU_out: REG +@00a _00_100_0000_110_xxxx_011_011__00__11_xxxx__11_001011 // ALU_1: 0 ALU_2: PARAM2 ([SP]) ALU_OP:ADD ALU_out: REG @00b _00_000_1100_011_0000_000_011__01__00_1100__00_000000 // ALU_1: PARAM1 (2) ALU_2: SP ALU_OP:ADD ALU_out: SP