Compare commits

...

2 Commits

12 changed files with 449 additions and 25 deletions

9
.gitignore vendored
View File

@ -5,12 +5,9 @@
*.bf.asm
*.swp
*.memdump
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
*.json
boot_code/*.bin
boot_code/*.txt
system/boot_code.bin
system/boot_code.txt
system/obj_dir/

View File

@ -21,7 +21,7 @@ VERILATOR_BIN=system/obj_dir/Vsystem
BOOT_CODE=boot_code/brainfuck_mandelbrot.txt
GTKWSAVE=./gtkwave_savefile.gtkw
MICROCODE=system/ucode.txt
BOOTABLES=boot_code/brainfuck_compiled.txt boot_code/brainfuck_interpreted.txt ${BOOT_CODE}
BOOTABLES=boot_code/brainfuck_compiled.txt boot_code/brainfuck_interpreted.txt ${BOOT_CODE} boot_code/fibonacci.txt boot_code/gnome_sort.txt
NO_ASM=1
include common.mk

View File

@ -1,9 +1,10 @@
SOURCE=brainfuck_interpreted.asm brainfuck_compiled.asm brainfuck_mandelbrot.asm
SOURCE=brainfuck_interpreted.asm brainfuck_compiled.asm brainfuck_mandelbrot.asm fibonacci.asm gnome_sort.asm
BINARIES=$(subst .asm,.txt,${SOURCE})
BUILD_FILES=${BINARIES}
BUILD_FILES+=$(subst .asm,.memdump,${SOURCE})
BUILD_FILES+=$(subst .asm,.fst,${SOURCE})
BUILD_FILES+=$(subst .asm,.bin,${SOURCE})
BUILD_FILES+=$(subst .asm,.json,${SOURCE})
all: ${BINARIES}
@ -11,6 +12,9 @@ brainfuck_interpreted.bin: brainfuck_interpreter_v0.asm hello_9086.bf.asm dos_la
brainfuck_compiled.bin: brainfuck_compiler_v1.asm hello_9086.bf.asm dos_layer.asm
brainfuck_mandelbrot.bin: brainfuck_compiler_v1.asm mandelbrot.bf.asm dos_layer.asm
fibonacci.bin: helpers.asm
gnome_sort.bin: helpers.asm
%.bf.asm:%.bf
${Q}sed "s/[a-zA-Z\* ]//g;/^$$/d;s/^/.ASCII '/;s/\$$/'/" "$^" > $@

38
boot_code/fibonacci.asm Normal file
View File

@ -0,0 +1,38 @@
INCLUDE dos_layer.asm
org 0x100
mov sp,#STACK
MOV AX,#0x1
MOV BX,#0x1
CALL PRINT_16_HEX
push bx
MAIN_LOOP:
pop bx
CALL PRINT_16_HEX
push AX
ADD AX,BX
JNC MAIN_LOOP
pop bx
MOV AH,#0x02
MOV DL,#0x0a
INT #0x21
hlt
.BLKB 200
STACK:
INCLUDE helpers.asm
.ORG 0xFFF0
MOV AX,#0x0100
JMP AX

54
boot_code/gnome_sort.asm Normal file
View File

@ -0,0 +1,54 @@
INCLUDE dos_layer.asm
.org 0x100
mov sp,#STACK
MOV SI,#DATA
GNOME_SORT:
CMP SI,#DATA+31
JZ GNOMED
MOV AX,[SI]
INC SI
CMP AH,AL
JAE GNOME_SORT
SWAP:
MOV BL,AL
MOV AL,AH
MOV AH,BL
DEC SI
MOV [SI],AX
CMP SI,#DATA
JZ GNOME_SORT
DEC SI
JMP GNOME_SORT
GNOMED:
MOV SI,#DATA
PRINT_LOOP:
MOV AL,[SI]
call PRINT_0_8_HEX
INC SI
CMP SI,#DATA+32
JNZ PRINT_LOOP
MOV AH,#0x02
MOV DL,#0x0a
INT #0x21
hlt
DATA: DB 0x51, 0x17, 0x37, 0x5d, 0x06, 0x3f, 0x51, 0x8b
DB 0xa5, 0x33, 0x54, 0xdf, 0xae, 0xee, 0x3a, 0x18
DB 0xe9, 0xdb, 0x1f, 0x21, 0x44, 0x4f, 0x99, 0x09
DB 0x2a, 0x23, 0x82, 0x4f, 0x52, 0xf1, 0xdc, 0x0b
.BLKB 200
STACK:
INCLUDE helpers.asm
.ORG 0xFFF0
MOV AX,#0x0100
JMP AX

93
boot_code/helpers.asm Normal file
View File

@ -0,0 +1,93 @@
;Input AX
PRINT_16_HEX:
PUSH DX
TEST AH,#0xF0
jz NOT_FIRST_NIBBLE
MOV DL,AH
CALL PRINT_HIGH
JMP SKIP1
NOT_FIRST_NIBBLE:
TEST AH,#0x0F
jz NOT_SECOND_NIBBLE
SKIP1:
MOV DL,AH
CALL PRINT_LOW
JMP SKIP2
NOT_SECOND_NIBBLE:
TEST AL,#0xF0
jz NOT_THIRD_NIBBLE
SKIP2:
MOV DL,AL
CALL PRINT_HIGH
NOT_THIRD_NIBBLE:
MOV DL,AL
CALL PRINT_LOW
PUSH AX
MOV AH,#0x02
MOV DL,#0x20
INT #0x21
POP AX
POP DX
RET
PRINT_HIGH:
AND DL,#0xF0
TEST DL,#0x80
jz NOT1
OR DL,#0x08
NOT1:
TEST DL,#0x40
jz NOT2
OR DL,#0x04
NOT2:
TEST DL,#0x20
jz NOT3
OR DL,#0x02
NOT3:
TEST DL,#0x10
jz DONE
OR DL,#0x01
DONE:
PRINT_LOW:
PUSH AX
AND DL,#0x0F
CMP DL,#0x0A
JNS LETTERS
ADD DL,#0x30
MOV AH,#0x02
INT #0x21
POP AX
RET
LETTERS:
ADD DL,#0x37
MOV AH,#0x02
INT #0x21
POP AX
RET
PRINT_0_8_HEX:
MOV DL,AL
PUSH AX
CALL PRINT_HIGH
POP AX
MOV DL,AL
CALL PRINT_LOW
PUSH AX
MOV AH,#0x02
MOV DL,#0x20
INT #0x21
POP AX
RET

View File

@ -57,6 +57,10 @@ ifeq "${SIM}" "ICARUS"
%.run: %.txt ${SYSTEM_VVP} ${MICROCODE}
${QUIET_VVP}
${Q}vvp -i "${SYSTEM_VVP}" +BOOT_CODE="$<" +MICROCODE="${MICROCODE}"
%.json: %.txt ${SYSTEM_VVP} ${MICROCODE}
${QUIET_VVP}
${Q}vvp -i "${SYSTEM_VVP}" +STATS="$@" +BOOT_CODE="$<" +MICROCODE="${MICROCODE}"
else ifeq "${SIM}" "VERILATOR"
%.fst %.memdump: %.txt ${VERILATOR_BIN} ${MICROCODE}
$(call QUIET_VERILATOR_RUN,$(word 2,$^),$<)
@ -64,6 +68,10 @@ else ifeq "${SIM}" "VERILATOR"
${Q}grep -v '^//' "$(subst .txt,.memdumptxt,$<)" | xxd -ps -c 2 -r > "$(subst .txt,.memdump,$<)"
${Q}rm "$(subst .txt,.memdumptxt,$<)"
%.json: %.txt ${VERILATOR_BIN} ${MICROCODE}
$(call QUIET_VERILATOR_RUN,$(word 2,$^),$<)
${Q} ${NUMACTL} "${VERILATOR_BIN}" +STATS=$@ +BOOT_CODE="$<" +MICROCODE="${MICROCODE}"
%.run: %.txt ${VERILATOR_BIN} ${MICROCODE}
$(call QUIET_VERILATOR_RUN,$(word 2,$^),$<)
${Q} ${NUMACTL} "${VERILATOR_BIN}" +BOOT_CODE="$<" +MICROCODE="${MICROCODE}"

View File

@ -21,3 +21,12 @@
//`define DEBUG_REG_WRITES
//`define DEBUG_PC_ADDRESS
//`define DEBUG_MEMORY_WRITES
`define CALCULATE_IPC
`define OUTPUT_JSON_STATISTICS
/********** Internal **********/
`ifdef OUTPUT_JSON_STATISTICS
`define CALCULATE_IPC
`endif

View File

@ -184,19 +184,22 @@ always @( CIR or SIMPLE_MICRO or seq_addr_input ) begin
in_alu1_sel1=2'b00;
OUT_MOD=3'b100;
MEM_OR_IO=0;
ALU_1OP=`ALU_OP_SUB;
ALU_1OP=`ALU_OP_SUB_REVERSE;
memio_address_select=0;
if(IN_MOD==3'b011)begin
/*compare register with param*/
in_alu1_sel2=2'b01;
reg_read_port2_addr={Wbit,RM};
next_state=`PROC_DE_LOAD_8_PARAM;
end else begin
/*compare register indirect access
* with param */
in_alu1_sel2=2'b00;
next_state=`PROC_DE_LOAD_16_PARAM; /*will then call MEMIO_READ*/
/*will call MEMIO_READ after EXEC_DE_LOAD..*/
end
if (Wbit)
next_state=`PROC_DE_LOAD_16_PARAM;
else
next_state=`PROC_DE_LOAD_8_PARAM;
`normal_instruction;
end
11'b1011_0???_??? : begin
@ -364,7 +367,7 @@ always @( CIR or SIMPLE_MICRO or seq_addr_input ) begin
in_alu1_sel2=2'b01;
reg_read_port2_addr={Wbit,3'b000};
OUT_MOD=3'b100;
ALU_1OP=`ALU_OP_SUB;
ALU_1OP=`ALU_OP_SUB_REVERSE;
MEM_OR_IO=0;
if(Wbit==1)
next_state=`PROC_DE_LOAD_16_PARAM;
@ -423,6 +426,13 @@ always @( CIR or SIMPLE_MICRO or seq_addr_input ) begin
else
next_state=`PROC_EX_STATE_ENTRY;
end
3'b001: begin
/* Jump on (not) Carry */
if(FLAGS[0:0]==CIR[8:8])
next_state=`PROC_IF_STATE_ENTRY;
else
next_state=`PROC_EX_STATE_ENTRY;
end
default:begin
`invalid_instruction; /*We don't support that condition*/
end
@ -526,6 +536,29 @@ always @( CIR or SIMPLE_MICRO or seq_addr_input ) begin
`normal_instruction;
memio_address_select=0;
end
11'b1010_100?_???:begin
/* TEST - Bitwise AND of immediate and accumulator affecting only flags */
/* 1 0 1 0 1 0 0 W | DATA | DATA if W | */
opcode_size=0;
Wbit=CIR[8:8];
IN_MOD=3'b011;
RM=3'b000;
MEM_OR_IO=0;
if(Wbit==1)begin
instruction_size=3;
next_state=`PROC_DE_LOAD_16_PARAM;
end else begin
instruction_size=2;
next_state=`PROC_DE_LOAD_8_PARAM;
end
in_alu1_sel1=2'b00; /* PARAM1 */
ALU_1OP=`ALU_OP_AND;
in_alu1_sel2=2'b01;
reg_read_port2_addr={Wbit,RM};
OUT_MOD=3'b100;/*NULL*/
`normal_instruction;
memio_address_select=0;
end
11'b0101_1???_???:begin
/* POP - REG=[SP]; SP+=2 */
/* | 0 1 0 1 1 REG | */
@ -629,6 +662,90 @@ always @( CIR or SIMPLE_MICRO or seq_addr_input ) begin
seq_addr_entry<=`UCODE_RET_ENTRY;
memio_address_select=0;
end
11'b1000_000?_100,11'b1000_000?_001:begin
/* OR - Bitwise OR immediate and register/mem */
/* 1 0 0 0 0 0 0 W | MOD 0 0 1 R/M | < DISP-LO > | < DISP-HI > | DATA | DATA if W */
/* AND - Bitwise AND immediate and register/mem */
/* 1 0 0 0 0 0 0 W | MOD 1 0 0 R/M | < DISP-LO > | < DISP-HI > | DATA | DATA if W */
opcode_size=1;
Wbit=CIR[8:8];
IN_MOD={1'b0,CIR[7:6]};
RM={CIR[2:0]};
MEM_OR_IO=0;
if(Wbit==1)begin
instruction_size=4;
next_state=`PROC_DE_LOAD_16_PARAM;
end else begin
instruction_size=3;
next_state=`PROC_DE_LOAD_8_PARAM;
end
in_alu1_sel1=2'b00; /* PARAM1 */
case(CIR[5:3])
3'b100: ALU_1OP=`ALU_OP_AND;
3'b001: ALU_1OP=`ALU_OP_OR;
default:begin end
endcase
case(IN_MOD)
3'b011:begin
in_alu1_sel2=2'b01;
reg_read_port2_addr={Wbit,RM};
reg_write_addr={Wbit,RM};
`normal_instruction;
end
default:begin
`invalid_instruction;
end
endcase
OUT_MOD=IN_MOD;
memio_address_select=0;
end
11'b0000_00??_???,11'b0010_10??_???,11'b0011_10??_???:begin
/* CMP - Compare Register/memory and register */
/* 0 0 1 1 1 0 D W | MOD REG R/M | < DISP LO > | < DISP HI > | */
/* SUB - Reg/memory with register to either */
/* 0 0 1 0 1 0 D W | MOD REG R/M | < DISP LO > | < DISP HI > | */
/* ADD - Reg/memory with register to either */
/* 0 0 0 0 0 0 D W | MOD REG R/M | < DISP LO > | < DISP HI > | */
instruction_size=2;
opcode_size=1;
Wbit=CIR[8:8];
Sbit=0;
IN_MOD=3'b011;
RM=CIR[2:0];
in_alu1_sel1=2'b01;//constantly register
reg_read_port1_addr={Wbit,CIR[5:3]};
if(IN_MOD==3'b011)begin
in_alu1_sel2=2'b01;
reg_read_port2_addr={Wbit,RM};
reg_write_addr={Wbit,RM};
next_state=`PROC_EX_STATE_ENTRY;
end else begin
in_alu1_sel2=2'b00;
if(Wbit)
next_state=`PROC_DE_LOAD_16_PARAM;
else
next_state=`PROC_DE_LOAD_8_PARAM;
end
MEM_OR_IO=0;
memio_address_select=0;
case (CIR[13:10])
4'b0000: ALU_1OP=`ALU_OP_ADD;
4'b1010: ALU_1OP=`ALU_OP_SUB;
4'b1110: ALU_1OP=`ALU_OP_SUB_REVERSE;
default: begin end
endcase
case (CIR[13:10])
4'b0000: OUT_MOD={1'b0,CIR[7:6]};
4'b1010: OUT_MOD={1'b0,CIR[7:6]};
4'b1110: OUT_MOD=3'b100; /* NULL */
default: begin end
endcase
if(CIR[9:9]==1'b0) begin
`normal_instruction;
end else begin
`invalid_instruction
end
end
default:begin
`invalid_instruction
end

View File

@ -29,7 +29,17 @@
//read: active low
//reset: active low
module processor ( input clock, input reset, output reg [19:0] external_address_bus, inout [15:0] external_data_bus,output reg read, output reg write,output reg BHE,output reg IOMEM, output reg HALT,output reg ERROR);
module processor (
/* MISC */ input clock, input reset, output reg HALT,output reg ERROR
/* MEMORY / IO */ ,output reg [19:0] external_address_bus, inout [15:0] external_data_bus,output reg read, output reg write,output reg BHE,output reg IOMEM
`ifdef CALCULATE_IPC
/* STATISTICS */ ,output reg new_instruction
`endif
`ifdef OUTPUT_JSON_STATISTICS
/* */ , output reg jump_debug
`endif
);
/*if we don't read, output the register to have the bus stable by the write falling edge*/
reg [15:0] data_bus_output_register;
@ -198,6 +208,12 @@ always @(posedge clock) begin
reg_write_we <= 1;
instruction_size_init <= 1;
state <= `PROC_IF_STATE_ENTRY;
`ifdef CALCULATE_IPC
new_instruction <= 0;
`endif
`ifdef OUTPUT_JSON_STATISTICS
jump_debug <= 0;
`endif
end
`PROC_HALT_STATE:begin
end
@ -210,6 +226,9 @@ always @(posedge clock) begin
reg_write_we <= 1;
state <= `PROC_IF_WRITE_CIR;
reg_write_in_sel <= 2'b00;
`ifdef OUTPUT_JSON_STATISTICS
jump_debug <= 0;
`endif
end
`PROC_IF_WRITE_CIR:begin
`ifdef DEBUG_PC_ADDRESS
@ -224,6 +243,9 @@ always @(posedge clock) begin
$display("Fetched instruction at %0x",ProgCount - 0);
end
`endif
`ifdef CALCULATE_IPC
new_instruction <= !new_instruction;
`endif
/*I built the entire decode stage with CIR
* being big endian so just convert it here*/
@ -523,6 +545,9 @@ always @(posedge clock) begin
state <= `PROC_NEXT_MICROCODE;
end
3'b101:begin /* Program Counter*/
`ifdef OUTPUT_JSON_STATISTICS
jump_debug <= 1;
`endif
ProgCount <= ALU_1O[15:0];
instruction_size_init <= 1;
if (ucode_seq_addr==`UCODE_NO_INSTRUCTION)
@ -609,6 +634,9 @@ always @(posedge clock) begin
state <= `PROC_NEXT_MICROCODE;
end
`PROC_NEXT_MICROCODE:begin
`ifdef OUTPUT_JSON_STATISTICS
jump_debug <= 0;
`endif
read <= 0;
write <= 1; // maybe we are coming from MEMIO_WRITE
BHE <= 0;

View File

@ -18,31 +18,78 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
`timescale 1ns/1ps
`include "config.v"
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);
`ifdef CALCULATE_IPC
wire new_instruction;
`endif
`ifdef OUTPUT_JSON_STATISTICS
wire jump;
`endif
processor p(
/* MISC */ clock,reset,HALT,ERROR
/* MEMORY / IO */ ,address_bus,data_bus,rd,wr,BHE,IOMEM
`ifdef CALCULATE_IPC
/* STATISTICS */ ,new_instruction
`endif
`ifdef OUTPUT_JSON_STATISTICS
/* */ , jump
`endif
);
doublemem sysmem(address_bus,data_bus,rd,wr,BHE,IOMEM);
`ifdef OUTPUT_JSON_STATISTICS
string stats_name;
integer json_file_descriptor;
`endif
string waveform_name;
initial begin
if($value$plusargs("WAVEFORM=%s",waveform_name))begin
$dumpfile(waveform_name);
$dumpvars(0,p);
end
`ifdef OUTPUT_JSON_STATISTICS
if($value$plusargs("STATS=%s",stats_name))begin
json_file_descriptor=$fopen(stats_name,"w");
$fdisplay(json_file_descriptor,"{\n\"L1_size\":0,\n\"Cycles\":[");
first_json_cycle = 1;
end else
json_file_descriptor=0;
`endif
end
`ifdef OUTPUT_JSON_STATISTICS
reg first_json_cycle;
always @(negedge clock)begin
if(HALT==0 && json_file_descriptor!=0)begin
$fdisplay(json_file_descriptor,"%s{\"C\":%0d,\"JMP\":%0d}",first_json_cycle?"":",",cycles,jump);
first_json_cycle <= 0;
end
end
`endif
always @(negedge wr) begin
if(IOMEM==1'b1 && address_bus[7:0]==8'hA5 )
$write("%s" ,data_bus[15:8]);
end
`ifdef CALCULATE_IPC
reg [128:0] instruction_count;
always @(new_instruction) begin
instruction_count<=instruction_count+1;
end
`endif
reg [1:0] finish;
string memdump_name;
always @(posedge HALT) begin
$display("Processor halted.\nCycles run for: %d",cycles-1);
if($value$plusargs("MEMDUMP=%s",memdump_name))begin
$writememh(memdump_name, sysmem.memory,0,32767);
end
@ -53,7 +100,17 @@ always @(posedge clock) begin
/* Allow some clock cycles for the waveform*/
case(finish)
2'd0: begin end
2'd1: finish <= 2;
2'd1: begin
finish <= 2;
$display("\x1b[7mProcessor halted.\nCycles run for : %0d\x1b[m",cycles);
`ifdef CALCULATE_IPC
$display("\x1b[7mInstr. per cycle : %f\x1b[m", $itor(instruction_count) / $itor(cycles) );
`endif
`ifdef OUTPUT_JSON_STATISTICS
if(json_file_descriptor!=0)
$fdisplay(json_file_descriptor,"],\n\"Total Cycles\":%0d,\n\"Instructions run\":%0d\n}",cycles,instruction_count);
`endif
end
2'd2: finish <= 3;
2'd3: $finish;
endcase
@ -67,13 +124,17 @@ always @(posedge ERROR) begin
finish<=2'd1;
end
integer cycles=0;
reg [128:0] cycles=0;
always @(posedge clock)begin
always @(negedge clock)begin
if(reset==1)
cycles<=cycles+1;
else
else begin
cycles<=0;
`ifdef CALCULATE_IPC
instruction_count <= 0;
`endif
end
end

View File

@ -46,13 +46,28 @@ clock_gen #(.FREQ(1000)) u1(clk_enable, clock);
string memdump_name;
initial begin
clk_enable = 1;
do_reset = 0;
reset<=1;
end
reset = 1;
#(`CPU_SPEED*2)
reset = 0;
#($random%1000)
#(`CPU_SPEED)
reset = 1;
reg [1:0]do_reset;
always @(posedge clock) begin
case(do_reset)
2'd0:begin
do_reset<=1;
end
2'd1:begin
do_reset<=2;
reset <= 0;
end
2'd2:begin
do_reset<=3;
reset <= 1;
end
2'd3:begin
end
endcase
end
endmodule