Cleaned the decoder a bit and laid down some of the groundwork for microcode

This commit is contained in:
(Tim) Efthimis Kritikos 2023-02-19 16:22:23 +00:00
parent e6c9c722e3
commit e2e9a92832
7 changed files with 199 additions and 240 deletions

View File

@ -19,6 +19,7 @@
SYSTEM_VVP=system/system.vvp SYSTEM_VVP=system/system.vvp
BOOT_CODE=boot_code/brainfuck.txt BOOT_CODE=boot_code/brainfuck.txt
GTKWSAVE=./gtkwave_savefile.gtkw GTKWSAVE=./gtkwave_savefile.gtkw
MICROCODE=system/ucode.hex
include common.mk include common.mk

View File

@ -11,6 +11,7 @@ A CPU that aims to be binary compatible with the 8086 and with as many optimisat
* [ ] Is pipelined * [ ] Is pipelined
* [ ] Is Out of Order * [ ] Is Out of Order
* [ ] Is superscalar * [ ] Is superscalar
* [ ] Has been successfully synthesized
### Simulating it ### Simulating it
To simulate this project you need Icarus Verilog, bin86, GNU make, xxd and the posix coreutils. To simulate this project you need Icarus Verilog, bin86, GNU make, xxd and the posix coreutils.

View File

@ -34,13 +34,13 @@ disas: $(subst .txt,.disas,${BOOT_CODE})
# Running simulation # Running simulation
%.lx2 %.memdump: %.txt ${SYSTEM_VVP} %.lx2 %.memdump: %.txt ${SYSTEM_VVP}
${QUIET_VVP} ${QUIET_VVP}
${Q}vvp "${SYSTEM_VVP}" -lxt2 +BOOT_CODE="$<" +WAVEFORM="$(subst .txt,.lx2,$<)" +MEMDUMP="$(subst .txt,.memdumptxt,$<)" ${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}grep -v '^//' "$(subst .txt,.memdumptxt,$<)" | xxd -ps -c 2 -r > "$(subst .txt,.memdump,$<)"
${Q}rm "$(subst .txt,.memdumptxt,$<)" ${Q}rm "$(subst .txt,.memdumptxt,$<)"
%.run: %.txt ${SYSTEM_VVP} %.run: %.txt ${SYSTEM_VVP}
${QUIET_VVP} ${QUIET_VVP}
${Q}vvp -i "${SYSTEM_VVP}" +BOOT_CODE="$<" ${Q}vvp -i "${SYSTEM_VVP}" +BOOT_CODE="$<" +MICROCODE="${MICROCODE}"
%.disas: %.bin %.disas: %.bin
objdump -D -b binary -m i8086 $^ | less objdump -D -b binary -m i8086 $^ | less

View File

@ -16,10 +16,11 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
SOURCES=processor.v testbench.v memory.v registers.v alu.v decoder.v general.v 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 INCLUDES=proc_state_def.v alu_header.v config.v ucode_header.v ucode.hex
SYSTEM_VVP=system.vvp SYSTEM_VVP=system.vvp
BOOT_CODE=boot_code.txt BOOT_CODE=boot_code.txt
GTKWSAVE=../gtkwave_savefile.gtkw GTKWSAVE=../gtkwave_savefile.gtkw
MICROCODE=ucode.hex
include ../common.mk include ../common.mk

View File

@ -19,7 +19,28 @@
`include "proc_state_def.v" `include "proc_state_def.v"
`include "alu_header.v" `include "alu_header.v"
`include "ucode_header.v"
module microcode(
input [`UCODE_ADDR_BITS-1:0] ADDR,
output [`UCODE_DATA_BITS-1:0] DATA
);
initial begin
string ucode_path;
if($value$plusargs("MICROCODE=%s",ucode_path))begin
$readmemb(ucode_path,ucode,0,`UCODE_SIZE-1);
end else begin
$display("Please supply microcode rom file as a runtime vvp argument +MICROCODE=<path>");
$finish;
end
end
reg [`UCODE_DATA_BITS-1:0] ucode [ 0:`UCODE_SIZE-1 ];
assign DATA=ucode[ADDR];
endmodule
module decoder( module decoder(
input wire [15:0] CIR,input wire [15:0] FLAGS, output reg Wbit, output reg Sbit, output reg unaligning ,output reg opcode_size, output reg ERROR,output reg [`PROC_STATE_BITS-1:0]next_state input wire [15:0] CIR,input wire [15:0] FLAGS, output reg Wbit, output reg Sbit, output reg unaligning ,output reg opcode_size, output reg ERROR,output reg [`PROC_STATE_BITS-1:0]next_state
@ -29,6 +50,10 @@ module decoder(
,output reg [2:0]ALU_1OP ,output reg [2:0]ALU_1OP
); );
wire [`UCODE_DATA_BITS-1:0] ucode_data;
reg [`UCODE_ADDR_BITS-1:0] UCODE_ADDR;
microcode ucode(UCODE_ADDR,ucode_data);
/* 0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0 */ /* 0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0 */
`define invalid_instruction next_state=`PROC_IF_STATE_ENTRY;ERROR=1;MOD=2'b11; `define invalid_instruction next_state=`PROC_IF_STATE_ENTRY;ERROR=1;MOD=2'b11;
@ -38,94 +63,79 @@ module decoder(
always @( CIR ) begin always @( CIR ) begin
ERROR=0;HALT=0; ERROR=0;HALT=0;
case(CIR[15:10]) casex({CIR[15:8],CIR[5:3]})
6'b000001 : begin 11'b0000_010x_xxx : begin
/* ADD, ... */ /* Add Immediate word/byte to accumulator */
if ( CIR[9:9] == 0 )begin /* 0 0 0 0 0 1 0 W | DATA | DATA if W |*/
/* Add Immediate word/byte to accumulator */ opcode_size=0;
/* 0 0 0 0 0 1 0 W | DATA | DATA if W |*/ has_operands=1;
opcode_size=0; Wbit=CIR[8:8];
has_operands=1; if(Wbit)
Wbit=CIR[8:8]; `start_unaligning_instruction
if(Wbit) else
`start_unaligning_instruction `start_aligning_instruction
else MOD=2'b11;
`start_aligning_instruction in_alu1_sel1=2'b00;
MOD=2'b11; in_alu1_sel2=2'b01;
out_alu1_sel=3'b011;
reg_read_port1_addr={Wbit,3'b000};
reg_write_addr={Wbit,3'b000};
ALU_1OP=`ALU_OP_ADD;
if(Wbit==1)
next_state=`PROC_DE_LOAD_16_PARAM;
else begin
PARAM1[7:0]=CIR[7:0];
next_state=`PROC_EX_STATE_ENTRY;
end
end
11'b1000_00xx_000 : begin
/* Add Immediate word/byte to register/memory */
/* 1 0 0 0 0 0 S W | MOD 0 0 0 R/M | < DISP LO > | < DISP HI > | DATA | DATA if W | */
`start_aligning_instruction
opcode_size=1;
has_operands=1;
Wbit=CIR[8:8];
Sbit=CIR[9:9];
MOD=2'b11;
in_alu1_sel1=2'b00;
in_alu1_sel2=2'b01;
out_alu1_sel={1'b0,MOD};
reg_read_port1_addr={Wbit,RM};
reg_write_addr={Wbit,RM};
ALU_1OP=`ALU_OP_ADD;
next_state=`PROC_DE_LOAD_16_PARAM;
if(Wbit==1)
next_state=`PROC_DE_LOAD_16_PARAM;
else begin
`invalid_instruction /*do 8bit loads*/
end
end
11'b1000_00xx_111 : begin
/* CMP - compare Immediate with register / memory */
/* 1 0 0 0 0 0 S W | MOD 1 1 1 R/M | < DISP LO > | < DISP HI > | DATA | DATA if W | */
opcode_size=1;
has_operands=1;
Wbit=CIR[8:8];
Sbit=CIR[9:9];
MOD=CIR[7:6];
RM=CIR[2:0];
if(((Wbit==1)&&(Sbit==1))||Wbit==0)begin
`start_unaligning_instruction
end else begin
`invalid_instruction;
end
if(MOD==2'b11)begin
in_alu1_sel1=2'b00; in_alu1_sel1=2'b00;
in_alu1_sel2=2'b01; in_alu1_sel2=2'b01;
out_alu1_sel=3'b011; reg_read_port1_addr={Wbit,RM};
reg_read_port1_addr={Wbit,3'b000}; out_alu1_sel=3'b100;
reg_write_addr={Wbit,3'b000}; ALU_1OP=`ALU_OP_SUB;
ALU_1OP=`ALU_OP_ADD; next_state=`PROC_DE_LOAD_8_PARAM;
if(Wbit==1)
next_state=`PROC_DE_LOAD_16_PARAM;
else begin
PARAM1[7:0]=CIR[7:0];
next_state=`PROC_EX_STATE_ENTRY;
end
end else begin end else begin
`invalid_instruction `invalid_instruction
end end
end end
6'b100000 : begin 11'b1011_0xxx_xxx : begin
/* ADD, ADC, SUB, SBB, CMP , AND, ... */
case (CIR[5:3])
3'b000 : begin
/* Add Immediate word/byte to register/memory */
/* 1 0 0 0 0 0 S W | MOD 0 0 0 R/M | < DISP LO > | < DISP HI > | DATA | DATA if W | */
`start_aligning_instruction
opcode_size=1;
has_operands=1;
Wbit=CIR[8:8];
Sbit=CIR[9:9];
MOD=2'b11;
in_alu1_sel1=2'b00;
in_alu1_sel2=2'b01;
out_alu1_sel={1'b0,MOD};
reg_read_port1_addr={Wbit,RM};
reg_write_addr={Wbit,RM};
ALU_1OP=`ALU_OP_ADD;
next_state=`PROC_DE_LOAD_16_PARAM;
if(Wbit==1)
next_state=`PROC_DE_LOAD_16_PARAM;
else begin
`invalid_instruction /*do 8bit loads*/
end
end
3'b111 : begin
/* CMP - compare Immediate with register / memory */
/* 1 0 0 0 0 0 S W | MOD 1 1 1 R/M | < DISP LO > | < DISP HI > | DATA | DATA if W | */
opcode_size=1;
has_operands=1;
Wbit=CIR[8:8];
Sbit=CIR[9:9];
MOD=CIR[7:6];
RM=CIR[2:0];
if(((Wbit==1)&&(Sbit==1))||Wbit==0)begin
`start_unaligning_instruction
end else begin
`invalid_instruction;
end
if(MOD==2'b11)begin
in_alu1_sel1=2'b00;
in_alu1_sel2=2'b01;
reg_read_port1_addr={Wbit,RM};
out_alu1_sel=3'b100;
ALU_1OP=`ALU_OP_SUB;
next_state=`PROC_DE_LOAD_8_PARAM;
end else begin
`invalid_instruction
end
end
default:begin
`invalid_instruction
end
endcase
end
6'b101100,
6'b101101:begin
/* MOV - Move Immediate byte to register */ /* MOV - Move Immediate byte to register */
/* 1 0 1 1 W REG | DATA | DATA if W |*/ /* 1 0 1 1 W REG | DATA | DATA if W |*/
`start_aligning_instruction `start_aligning_instruction
@ -142,8 +152,7 @@ always @( CIR ) begin
ALU_1OP=`ALU_OP_ADD; ALU_1OP=`ALU_OP_ADD;
next_state=`PROC_EX_STATE_ENTRY; next_state=`PROC_EX_STATE_ENTRY;
end end
6'b101110, 11'b1011_1xxx_xxx : begin
6'b101111 : begin
/*MOV - Move Immediate word to register*/ /*MOV - Move Immediate word to register*/
`start_unaligning_instruction `start_unaligning_instruction
has_operands=1; has_operands=1;
@ -159,7 +168,7 @@ always @( CIR ) begin
next_state=`PROC_DE_LOAD_16_PARAM; next_state=`PROC_DE_LOAD_16_PARAM;
end end
6'b100010 : begin 11'b1000_10xx_xxx : begin
/* MOV - Reg/Mem to/from register */ /* MOV - Reg/Mem to/from register */
/* 1 0 0 0 1 0 D W | MOD REG RM | < DISP LO > | < DISP HI > |*/ /* 1 0 0 0 1 0 D W | MOD REG RM | < DISP LO > | < DISP HI > |*/
has_operands=0; has_operands=0;
@ -204,10 +213,7 @@ always @( CIR ) begin
ALU_1OP=`ALU_OP_ADD; ALU_1OP=`ALU_OP_ADD;
PARAM2=0; PARAM2=0;
end end
6'b010000,//INC 11'b0100_xxxx_xxx:begin//DEC
6'b010001,//INC
6'b010010,//DEC
6'b010011:begin//DEC
/* DEC - Decrement Register */ /* DEC - Decrement Register */
/* | 0 1 0 0 1 REG | */ /* | 0 1 0 0 1 REG | */
/* INC - Increment Register */ /* INC - Increment Register */
@ -229,97 +235,69 @@ always @( CIR ) begin
ALU_1OP=`ALU_OP_SUB; ALU_1OP=`ALU_OP_SUB;
next_state=`PROC_EX_STATE_ENTRY; next_state=`PROC_EX_STATE_ENTRY;
end end
6'b111111 : begin 11'b1111_111x_00x : begin
/* INC */ /* INC - Register/Memory */
if (CIR[9:9] == 1 ) begin /* 1 1 1 1 1 1 1 W | MOD 0 0 0 R/M | < DISP LO> | < DISP HI> */
case (CIR[5:3]) /* DEC - Register/Memory */
3'b000,3'b001 :begin /* 1 1 1 1 1 1 1 W | MOD 0 0 1 R/M | < DISP LO> | < DISP HI> */
/* INC - Register/Memory */ has_operands=1;
/* 1 1 1 1 1 1 1 W | MOD 0 0 0 R/M | < DISP LO> | < DISP HI> */ opcode_size=1;
/* DEC - Register/Memory */ `start_aligning_instruction
/* 1 1 1 1 1 1 1 W | MOD 0 0 1 R/M | < DISP LO> | < DISP HI> */ Wbit=CIR[8:8];
has_operands=1; MOD=CIR[7:6];
opcode_size=1; RM=CIR[2:0];
`start_aligning_instruction in_alu1_sel1=(MOD==2'b11)? 2'b01 : 2'b00;
Wbit=CIR[8:8]; in_alu1_sel2=2'b00;/* number 1 */
MOD=CIR[7:6]; PARAM2=1;
RM=CIR[2:0]; out_alu1_sel={1'b0,MOD};
in_alu1_sel1=(MOD==2'b11)? 2'b01 : 2'b00;
in_alu1_sel2=2'b00;/* number 1 */
PARAM2=1;
out_alu1_sel={1'b0,MOD};
/*in case MOD=11 */ /*in case MOD=11 */
reg_read_port1_addr={1'b0,RM}; reg_read_port1_addr={1'b0,RM};
reg_write_addr={1'b0,RM}; reg_write_addr={1'b0,RM};
ALU_1OP=(CIR[3:3]==1)?`ALU_OP_SUB:`ALU_OP_ADD; ALU_1OP=(CIR[3:3]==1)?`ALU_OP_SUB:`ALU_OP_ADD;
if ( MOD == 2'b11 ) if ( MOD == 2'b11 )
next_state=`PROC_EX_STATE_ENTRY; next_state=`PROC_EX_STATE_ENTRY;
else else
next_state=`RPOC_MEMIO_READ; next_state=`RPOC_MEMIO_READ;
end end
default:begin 11'b1111_0100_xxx : begin
`invalid_instruction /* HLT - Halt */
end /* 1 1 1 1 0 1 0 0 | */
endcase has_operands=0;
end else begin opcode_size=0;
`invalid_instruction `start_unaligning_instruction
MOD=2'b11;
HALT=1;
next_state=`PROC_HALT_STATE;
end
11'b0011_110x_xxx : begin
/* CMP - Compare Immediate with accumulator */
/* 0 0 1 1 1 1 0 W | DATA | DATA if W |*/
/* */
/* NOTE: 8086 doc doesn't show the third byte but the */
/* W flag and my assembler seem to disagree */
Wbit=CIR[8:8];
opcode_size=0;
has_operands=1;
if(Wbit)
`start_unaligning_instruction
else
`start_aligning_instruction
MOD=2'b11;
in_alu1_sel1=2'b00;
in_alu1_sel2=2'b01;
reg_read_port1_addr={Wbit,3'b000};
out_alu1_sel=3'b100;
ALU_1OP=`ALU_OP_SUB;
if(Wbit==1)
next_state=`PROC_DE_LOAD_16_PARAM;
else begin
PARAM1[7:0]=CIR[7:0];
next_state=`PROC_EX_STATE_ENTRY;
end end
end end
6'b111101 : begin 11'b0111_xxxx_xxx:begin
/*HLT, CMC, TEST, NOT, NEG, MUL, IMUL, .... */
case (CIR[9:8])
2'b00:begin
/* HLT - Halt */
/* 1 1 1 1 0 1 0 0 | */
has_operands=0;
opcode_size=0;
`start_unaligning_instruction
MOD=2'b11;
HALT=1;
next_state=`PROC_HALT_STATE;
end
default:begin
`invalid_instruction;
end
endcase
end
6'b001111 : begin
if ( CIR[9:9] == 0 ) begin
/* CMP - Compare Immediate with accumulator */
/* 0 0 1 1 1 1 0 W | DATA | DATA if W |*/
/* */
/* NOTE: 8086 doc doesn't show the third byte but the */
/* W flag and my assembler seem to disagree */
Wbit=CIR[8:8];
opcode_size=0;
has_operands=1;
if(Wbit)
`start_unaligning_instruction
else
`start_aligning_instruction
MOD=2'b11;
in_alu1_sel1=2'b00;
in_alu1_sel2=2'b01;
reg_read_port1_addr={Wbit,3'b000};
out_alu1_sel=3'b100;
ALU_1OP=`ALU_OP_SUB;
if(Wbit==1)
next_state=`PROC_DE_LOAD_16_PARAM;
else begin
PARAM1[7:0]=CIR[7:0];
next_state=`PROC_EX_STATE_ENTRY;
end
end else begin
`invalid_instruction
end
end
6'b011100,
6'b011101,
6'b011110,
6'b011111:begin
/* Conditional relative jumps */ /* Conditional relative jumps */
/* JE/JZ - Jump on Zero */ /* JE/JZ - Jump on Zero */
/* 0 1 1 1 0 1 0 0 | IP-INC8 |*/ /* 0 1 1 1 0 1 0 0 | IP-INC8 |*/
@ -372,68 +350,31 @@ always @( CIR ) begin
end end
endcase endcase
end end
6'b111010:begin 11'b1110_1011_xxx:begin
/* JMP,CALL */ /* JMP - Unconditional jump direct within segment (short) */
case(CIR[9:8]) /* | 1 1 1 0 1 0 1 1 | IP-INC-LO | */
2'b00: begin `start_aligning_instruction
/* CALL - Call direct within segment */ opcode_size=0;
/* 1 1 1 0 1 0 0 0 | IP-INC-LO | IP-INC-HI |*/ has_operands=1;
`invalid_instruction Wbit=1;
end in_alu1_sel1=2'b10;
2'b01: begin in_alu1_sel2=2'b00;
/* JMP - Unconditional Jump direct within segment */ PARAM2={{8{CIR[7:7]}},CIR[7:0]};
/* 1 1 1 0 1 0 0 1 | IP-INC-LO | IP-INC-HI |*/ ALU_1OP=`ALU_OP_ADD_SIGNED_B;
`invalid_instruction out_alu1_sel=3'b101;
end next_state=`PROC_EX_STATE_ENTRY;
2'b10: begin
/* JMP - Unconditional jump direct intersegment */
/* 0 0 0 0 0 0 0 0 | IP-LO | IP-HI | CS-LO | CS-HI | */
`invalid_instruction
end
2'b11: begin
/* JMP - Unconditional jump direct within segment (short) */
/* | 1 1 1 0 1 0 0 1 | IP-INC-LO | */
`start_aligning_instruction
opcode_size=0;
has_operands=1;
Wbit=1;
in_alu1_sel1=2'b10;
in_alu1_sel2=2'b00;
PARAM2={{8{CIR[7:7]}},CIR[7:0]};
ALU_1OP=`ALU_OP_ADD_SIGNED_B;
out_alu1_sel=3'b101;
next_state=`PROC_EX_STATE_ENTRY;
end
endcase
end end
6'b110011:begin 11'b1100_1101_xxx:begin
case(CIR[9:8]) /* INT - execute interrupt handler */
2'b00:begin /* 1 1 0 0 1 1 0 1 | DATA |*/
`invalid_instruction has_operands=1;
end opcode_size=0;
2'b01:begin `start_aligning_instruction
if(CIR[7:0]==8'h21) begin /* Emulate MS-DOS print routines */
/* INT - execute interrupt handler */ if(CIR[7:0]==8'h21 && register_file.registers[0][15:8]==8'h02)begin
/* 1 1 0 0 1 1 0 1 | DATA |*/ $write("%s" ,register_file.registers[2][7:0]); /*TODO:Could trigger erroneously while CIR is not final*/
has_operands=1; end
opcode_size=0; next_state=`PROC_IF_STATE_ENTRY;
`start_aligning_instruction
/* Emulate MS-DOS print routines */
if(register_file.registers[0][15:8]==8'h02)begin
$write("%s" ,register_file.registers[2][7:0]); /*TODO:Could trigger erroneously while CIR is not final*/
end
next_state=`PROC_IF_STATE_ENTRY;
end else begin
`invalid_instruction
end
end
2'b10:begin
`invalid_instruction
end
2'b11:begin
`invalid_instruction
end
endcase
end end
default:begin default:begin
`invalid_instruction `invalid_instruction

9
system/ucode.hex Normal file
View File

@ -0,0 +1,9 @@
//s : Opcode size 0:8bit 1:16bit
//h : Has operands as part of the instruction: 0: No 1: Yes
// |sh
@000 01_11
@001 11_10
@002 1000
@003 0010

6
system/ucode_header.v Normal file
View File

@ -0,0 +1,6 @@
`define UCODE_ADDR_BITS 9
`define UCODE_DATA_BITS 32
`define UCODE_SIZE 4
`define ADD_AL_IB 0
`define ADD_AX_IW 1