Fixed copyright notices, did some major cleanup and bumped README's versions

This commit is contained in:
(Tim) Efthimis Kritikos 2023-05-11 16:28:10 +01:00
parent a8ab6b2dc7
commit 539fb8416b
6 changed files with 281 additions and 302 deletions

View File

@ -19,11 +19,11 @@ A CPU that aims to be binary compatible with the 8086 and with as many optimisat
Both Verilator and Icarus Verilog can be used for simulation. You can select which one you want with the SIM variable on [common.mk](./common.mk)
Specifically this list shows the software needed and the versions used during development (other versions should work as well)
* Icarus Verilog : version 11.0 OR **(preferred)** Verilator : 5.006
* Icarus Verilog : version 11.0 OR **(preferred)** Verilator : 5.008
* bin86 : 0.16.21
* GNU Make : 4.4.1
* xxd : 2022-01-14
* POSIX coreutils : GNU coreutils 9.1
* POSIX coreutils : GNU coreutils 9.3
After that you can run `make` on the top level directory and it should build everything and start the simulation

View File

@ -1,4 +1,4 @@
/* processor.v - implementation of the 9086 bus interface unit. The logic that
/* biu.v - implementation of the 9086 bus interface unit. The logic that
controls all external bus functions
This file is part of the 9086 project.
@ -18,7 +18,7 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
//IOMEM: 1=IO 0=MEM
`include "config.v"
`define BIU_HALT 4'b0000
`define BIU_NEXT_ACTION 4'b0001
@ -38,9 +38,9 @@
`define BIU_GET_SECOND_BYTE1 4'b1110
module BIU (
/*outside world*/ input clock, input reset, output reg [19:0] external_address_bus,
/* Outside world */ 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,
/* internal */ output reg [31:0] INSTRUCTION, output reg VALID_INSTRUCTION, output reg [15:0] INSTRUCTION_LOCATION, input jump_req,
/* Internal */ output reg [31:0] INSTRUCTION, output reg VALID_INSTRUCTION, output reg [15:0] INSTRUCTION_LOCATION, input jump_req,
/* */ input[15:0] ADDRESS_INPUT, inout [15:0] DATA, input write_request, input read_request, input Wbit, output reg VALID_DATA, input MEM_OR_IO,
/* */ input [`PROC_STATE_BITS-1:0] proc_state, input SIMPLE_MICRO
);
@ -52,9 +52,8 @@ reg [15:0] DATA_OUT;
reg DATA_DIR;
assign DATA=DATA_DIR ? 16'hz:DATA_OUT;
`define L1_CACHE_SIZE 4 // Don't change this! some parts of the code assume this
`define FIFO_SIZE_BYTES $rtoi($pow(2,`L1_CACHE_SIZE))
`define FIFO_SIZE_BYTES 16 //$pow(2,`L1_CACHE_SIZE)
reg [7:0] INPUT_FIFO [`FIFO_SIZE_BYTES-1:0];
reg [`L1_CACHE_SIZE-1:0] FIFO_start; /*inclusive*/
reg [`L1_CACHE_SIZE-1:0] FIFO_end; /*exclusive*/
@ -65,7 +64,6 @@ reg [3:0] biu_state;
always @(negedge reset) begin
biu_state <= `BIU_HALT;
end
always @(posedge reset) begin
biu_state <= `BIU_RESET;
end
@ -76,10 +74,8 @@ reg func;
reg [19:0]INSTRUCTION_ADDRESS;
reg [19:0]DATA_ADDRESS;
assign external_address_bus= func? INSTRUCTION_ADDRESS : DATA_ADDRESS ;
/* Read into the FIFO */
always @(posedge clock) begin
if ( jump_req_latch ) begin
/* verilator lint_off BLKSEQ */
@ -145,33 +141,30 @@ always @(posedge clock) begin
end
end
/*************** INSTRUCTION FIFO READ ***************/
`BIU_READ: begin
/* verilator lint_off BLKSEQ */
if(INSTRUCTION_ADDRESS[0:0]==0 && FIFO_SIZE<4'hD)begin
INPUT_FIFO[FIFO_end] = external_data_bus[7:0];
INPUT_FIFO[FIFO_end+4'd1] = external_data_bus[15:8];
INPUT_FIFO[FIFO_end] <= external_data_bus[7:0];
INPUT_FIFO[FIFO_end+4'd1] <= external_data_bus[15:8];
FIFO_end <= FIFO_end+4'd2;
INSTRUCTION_ADDRESS <= INSTRUCTION_ADDRESS+20'd2;
end else if(INSTRUCTION_ADDRESS[0:0]==0)begin
INPUT_FIFO[FIFO_end] = external_data_bus[7:0];
INPUT_FIFO[FIFO_end] <= external_data_bus[7:0];
FIFO_end <= FIFO_end+4'd1;
INSTRUCTION_ADDRESS <= INSTRUCTION_ADDRESS+1;
end else begin
INPUT_FIFO[FIFO_end] = external_data_bus[15:8];
INPUT_FIFO[FIFO_end] <= external_data_bus[15:8];
FIFO_end <= FIFO_end+4'd1;
INSTRUCTION_ADDRESS <= INSTRUCTION_ADDRESS+1;
end
/* verilator lint_on BLKSEQ */
biu_state <= `BIU_NEXT_ACTION;
read<=1;
end
/*************** DATA WRITE ***************/
//TODO TODO TODO flush fifo, self modifying code
`BIU_PUT_UNALIGNED_16BIT_DATA:begin
//TODO Put that in the previous stage, save a clock cycle
`ifdef DEBUG_DATA_READ_WRITES
$display("Writing 16bit %04x at %04x",DATA,DATA_ADDRESS);
`endif
@ -194,7 +187,6 @@ always @(posedge clock) begin
write <= 0;
biu_state <= `BIU_WRITE_RELEASE;
end
`BIU_PUT_BYTE:begin
`ifdef DEBUG_DATA_READ_WRITES
$display("Writing 8bit %02x at %04x",DATA[7:0],DATA_ADDRESS);
@ -288,10 +280,9 @@ end
wire [2:0] Isize;
InstrSize InstrSize({INSTRUCTION[31:24],INSTRUCTION[21:19]},Isize);
/* verilator lint_off UNDRIVEN */
wire [2:0] fifoIsize;
wire Isit1;
/* verilator lint_on UNDRIVEN */
`ifdef EARLY_VALID_INSTRUCTION
InstrSize fifoInstrSize({INPUT_FIFO[FIFO_start][7:0],INPUT_FIFO[FIFO_start+1][5:3]},fifoIsize);
Is1 Is1(INPUT_FIFO[FIFO_start][7:0],Isit1);
@ -310,16 +301,16 @@ always @( proc_state ) begin
was_dec<=0;
/* verilator lint_off BLKSEQ */
FIFO_start = FIFO_start + {1'b0,Isize};
INSTRUCTION_LOCATION <= INSTRUCTION_LOCATION + {12'b0,Isize};;
/* verilator lint_on BLKSEQ */
INSTRUCTION_LOCATION <= INSTRUCTION_LOCATION + {12'b0,Isize};;
VALID_INSTRUCTION <= 0;
end else if ( SIMPLE_MICRO==1 && was_simple == 1) begin
was_simple<=0;
was_dec<=0;
/* verilator lint_off BLKSEQ */
FIFO_start = FIFO_start + {1'b0,Isize};
INSTRUCTION_LOCATION <= INSTRUCTION_LOCATION + {12'b0,Isize};;
/* verilator lint_on BLKSEQ */
INSTRUCTION_LOCATION <= INSTRUCTION_LOCATION + {12'b0,Isize};;
VALID_INSTRUCTION <= 0;
end
end
@ -336,54 +327,3 @@ always @( posedge jump_req ) begin
end
endmodule
/* Pre-Decode the instruction size */
/* IN: {CIR[15:8],CIR[5:3]} */
/* OUT: number in bytes */
module InstrSize ( input [10:0] IN, output reg [2:0] VERDICT );
always @( IN ) begin
casez(IN)
11'b0000_010?_??? : VERDICT <= 3'd2+{2'b0,IN[3:3]}; /* ADD - Add Immediate word/byte to accumulator */
11'b1000_00??_101 : VERDICT <= 3'd3+{2'b0,(IN[4:3]==2'b01)}; /* SUB - Subtract immediate word/byte from register/memory */
11'b1000_00??_000 : VERDICT <= 3'd3+{2'b0,(IN[4:3]==2'b01)}; /* ADD - Add Immediate word/byte to register/memory */
11'b1000_00??_111 : VERDICT <= 3'd3+{2'b0,(IN[4:3]==2'b01)}; /* CMP - compare Immediate with register / memory */
11'b1011_????_??? : VERDICT <= 3'd2+{2'b0,IN[6:6]}; /* MOV - Move Immediate byte to register */
11'b1000_10??_??? : VERDICT <= 3'd2; /* MOV - Reg/Mem to/from register */
11'b0100_????_??? : VERDICT <= 3'd1; /* DEC - Decrement Register | INC - Increment Register */
11'b1111_111?_00? : VERDICT <= 3'd2; /* INC - Register/Memory | DEC - Register/Memory */
11'b1111_0100_??? : VERDICT <= 3'd1; /* HLT - Halt */
11'b0011_110?_??? : VERDICT <= 3'd2+{2'b0,IN[3:3]}; /* CMP - Compare Immediate with accumulator */
11'b0111_????_??? : VERDICT <= 3'd2; /* Conditional relative jumps ( JE/JZ, JS/JNS ... ) */
11'b1110_1011_??? : VERDICT <= 3'd2; /* JMP - Unconditional jump direct within segment (short) */
11'b1110_1000_??? : VERDICT <= 3'd3; /* CALL - Direct call within segment */
11'b1100_0011_??? : VERDICT <= 3'd1; /* RET - Return from call within segment */
11'b1010_101?_??? : VERDICT <= 3'd1; /* STOS - Write byte/word to [DI] and increment accordingly */
11'b0101_0???_??? : VERDICT <= 3'd1; /* PUSH - SP-=2; [SP]=REG */
11'b1111_011?_000 : VERDICT <= 3'd3+{2'b0,IN[3:3]}; /* TEST - Bitwise AND affecting only flags */
11'b0101_1???_??? : VERDICT <= 3'd1; /* POP - REG=[SP]; SP+=2 */
11'b1111_1111_100 : VERDICT <= 3'd2; /* JMP - Unconditional indirect within segment jump */
11'b1100_011?_000 : VERDICT <= 3'd3+{2'b0,IN[3:3]}; /* MOV - Move immediate to register/memory */
11'b1100_1101_??? : VERDICT <= 3'd2; /* INT - execute interrupt handler */
11'b1110_011?_??? : VERDICT <= 3'd2; /* OUT - write AL or AX to a defined output port */
11'b1100_1111_??? : VERDICT <= 3'd1; /* IRET - Return from interrupt */
default:begin end
endcase
end
endmodule
`ifdef EARLY_VALID_INSTRUCTION
module Is1 ( input [7:0] IN, output reg VERDICT );
always @( IN ) begin
casez(IN)
8'b0100_???? : VERDICT <= 1; /* DEC - Decrement Register | INC - Increment Register */
8'b1111_0100 : VERDICT <= 1; /* HLT - Halt */
8'b1100_0011 : VERDICT <= 1; /* RET - Return from call within segment */
8'b1010_101? : VERDICT <= 1; /* STOS - Write byte/word to [DI] and increment accordingly */
8'b0101_0??? : VERDICT <= 1; /* PUSH - SP-=2; [SP]=REG */
8'b0101_1??? : VERDICT <= 1; /* POP - REG=[SP]; SP+=2 */
8'b1100_1111 : VERDICT <= 1; /* IRET - Return from interrupt */
default:begin VERDICT<= 0; end
endcase
end
endmodule
`endif

View File

@ -29,9 +29,16 @@
* for the maximum instruction size worth of bytes */
`define EARLY_VALID_INSTRUCTION
/* Size is in powers of two with minimal 3.
* 3 : 8 Bytes
* 4 : 16 Bytes
* 5 : 32 Bytes
* . : ... */
`define L1_CACHE_SIZE 4 // Don't change it! some parts of code still assume it to be 4
/* Internal */
/********** Internal **********/
`ifdef EARLY_VALID_INSTRUCTION
`define EARLY_VALID_INSTRUCTION_ 1
`else

View File

@ -43,37 +43,64 @@ assign DATA=ucode_rom[ADDR];
endmodule
// verilator lint_off UNUSEDSIGNAL
module decoder(
input wire [15:0] CIR,input wire [15:0] FLAGS, output wire [2:0] INSTRUCTION_INFO, output wire [`ERROR_BITS:0] DECODER_SIGNALS, output reg [`EXEC_STATE_BITS-1:0] next_state
,output reg [2:0]IN_MOD, output reg [2:0]RM, output reg [15:0] PARAM1,output reg [15:0] PARAM2
,output reg [1:0]in_alu_sel1,output reg [1:0]in_alu_sel2,output reg [2:0]OUT_MOD
,output wire [11:0]REGISTER_FILE_CONTROL
,output reg [2:0]ALU_1OP
,output reg [`UCODE_ADDR_BITS-1:0] seq_addr_entry, input wire SIMPLE_MICRO, input wire [`UCODE_ADDR_BITS-1:0] seq_addr_input
,output reg memio_address_select
,output reg MEM_OR_IO
/* INPUTS */ input wire [15:0] CIR,input wire [15:0] FLAGS
/* MICROCODE */ ,output reg [`UCODE_ADDR_BITS-1:0] seq_addr_entry, input wire SIMPLE_MICRO, input wire [`UCODE_ADDR_BITS-1:0] seq_addr_input
/* OUTPUT */ ,output wire [`EXEC_STATE_BITS+`ERROR_BITS+65:0] OUTPUT
);
// verilator lint_on UNUSEDSIGNAL
reg [2:0]IN_MOD;
assign OUTPUT[2:0] = IN_MOD;
reg [2:0]RM;
assign OUTPUT[5:3] = RM;
reg memio_address_select;
assign OUTPUT[6:6] = memio_address_select;
reg MEM_OR_IO;
assign OUTPUT[7:7] = MEM_OR_IO;
reg [15:0] PARAM1;
assign OUTPUT[23:8] = PARAM1;
reg [15:0] PARAM2;
assign OUTPUT[39:24] = PARAM2;
reg [2:0]ALU_OP;
assign OUTPUT[42:40] = ALU_OP;
reg [1:0]in_alu_sel1;
assign OUTPUT[44:43] = in_alu_sel1;
reg [1:0]in_alu_sel2;
assign OUTPUT[46:45] = in_alu_sel2;
reg [2:0]OUT_MOD;
assign OUTPUT[49:47] = OUT_MOD;
reg [3:0]reg_read_port1_addr;
reg [3:0]reg_read_port2_addr;
reg [3:0]reg_write_addr;
assign REGISTER_FILE_CONTROL={reg_write_addr,reg_read_port1_addr,reg_read_port2_addr};
assign OUTPUT[53:50] = reg_read_port1_addr;
reg [3:0]reg_read_port2_addr;
assign OUTPUT[57:54] = reg_read_port2_addr;
reg [3:0]reg_write_addr;
assign OUTPUT[61:58] = reg_write_addr;
/* For correct fetching of instructions and global options for the alu */
reg Wbit,Sbit,opcode_size;
assign INSTRUCTION_INFO={Wbit,Sbit,opcode_size};
assign OUTPUT[64:62] = {Wbit,Sbit,opcode_size};
reg [`ERROR_BITS-1:0] ERROR;
reg HALT;
assign DECODER_SIGNALS={ERROR,HALT};
assign OUTPUT[`ERROR_BITS+65:65]={ERROR,HALT};
reg [`EXEC_STATE_BITS-1:0] next_state;
assign OUTPUT[`EXEC_STATE_BITS+`ERROR_BITS+65:`ERROR_BITS+66] = next_state;
// verilator lint_off UNUSEDSIGNAL
/* verilator lint_off UNUSEDSIGNAL */
wire [`UCODE_DATA_BITS-1:0] ucode_data;
// verilator lint_on UNUSEDSIGNAL
/* verilator lint_on UNUSEDSIGNAL */
microcode ucode(seq_addr_input,ucode_data);
@ -97,7 +124,7 @@ always @( FLAGS or CIR or SIMPLE_MICRO or seq_addr_input ) begin
11'b0000_010?_??? : begin
/* ADD - Add Immediate word/byte to accumulator */
/* 0 0 0 0 0 1 0 W | DATA | DATA if W |*/
opcode_size=0; //TODO: move the decoding done in BIU here and use theat size checker
opcode_size=0;
Wbit=CIR[8:8];
IN_MOD=3'b011;
in_alu_sel1=2'b00;
@ -106,7 +133,7 @@ always @( FLAGS or CIR or SIMPLE_MICRO or seq_addr_input ) begin
MEM_OR_IO=0;
reg_read_port2_addr={Wbit,3'b000};
reg_write_addr={Wbit,3'b000};
ALU_1OP=`ALU_OP_ADD;
ALU_OP=`ALU_OP_ADD;
memio_address_select=0;
if(Wbit)
next_state=`EXEC_DE_LOAD_16_PARAM;
@ -148,8 +175,8 @@ always @( FLAGS or CIR or SIMPLE_MICRO or seq_addr_input ) begin
end
endcase
case(CIR[5:3])
3'b000: ALU_1OP=`ALU_OP_ADD;
3'b101: ALU_1OP=`ALU_OP_SUB_REVERSE;
3'b000: ALU_OP=`ALU_OP_ADD;
3'b101: ALU_OP=`ALU_OP_SUB_REVERSE;
default:begin
/*Should be impossible*/
`invalid_instruction
@ -171,7 +198,7 @@ always @( FLAGS or CIR or SIMPLE_MICRO or seq_addr_input ) begin
in_alu_sel1=2'b00;
OUT_MOD=3'b100;
MEM_OR_IO=0;
ALU_1OP=`ALU_OP_SUB;
ALU_OP=`ALU_OP_SUB;
memio_address_select=0;
if(IN_MOD==3'b011)begin
/*compare register with param*/
@ -199,7 +226,7 @@ always @( FLAGS or CIR or SIMPLE_MICRO or seq_addr_input ) begin
reg_write_addr={1'b0,CIR[10:8]};
PARAM1[7:0]=CIR[7:0];
PARAM2=0;
ALU_1OP=`ALU_OP_ADD;
ALU_OP=`ALU_OP_ADD;
next_state=`EXEC_WRITE_ENTRY;
`normal_instruction;
memio_address_select=0;
@ -214,7 +241,7 @@ always @( FLAGS or CIR or SIMPLE_MICRO or seq_addr_input ) begin
OUT_MOD=3'b011;
MEM_OR_IO=0;
reg_write_addr={1'b1,CIR[10:8]};
ALU_1OP=`ALU_OP_ADD;
ALU_OP=`ALU_OP_ADD;
PARAM2=0;
next_state=`EXEC_DE_LOAD_16_PARAM;
`normal_instruction;
@ -260,7 +287,7 @@ always @( FLAGS or CIR or SIMPLE_MICRO or seq_addr_input ) begin
end
reg_read_port2_addr={Wbit,CIR[5:3]};
end
ALU_1OP=`ALU_OP_ADD;
ALU_OP=`ALU_OP_ADD;
`normal_instruction;
memio_address_select=0;
@ -281,9 +308,9 @@ always @( FLAGS or CIR or SIMPLE_MICRO or seq_addr_input ) begin
reg_read_port1_addr={1'b1,CIR[10:8]};
reg_write_addr={1'b1,CIR[10:8]};
if(CIR[11:11]==0)
ALU_1OP=`ALU_OP_ADD;
ALU_OP=`ALU_OP_ADD;
else
ALU_1OP=`ALU_OP_SUB;
ALU_OP=`ALU_OP_SUB;
next_state=`EXEC_WRITE_ENTRY;
`normal_instruction;
memio_address_select=0;
@ -307,7 +334,7 @@ always @( FLAGS or CIR or SIMPLE_MICRO or seq_addr_input ) begin
reg_read_port2_addr={1'b0,RM};
reg_write_addr={1'b0,RM};
ALU_1OP=(CIR[3:3]==1)?`ALU_OP_SUB_REVERSE:`ALU_OP_ADD;
ALU_OP=(CIR[3:3]==1)?`ALU_OP_SUB_REVERSE:`ALU_OP_ADD;
if ( IN_MOD == 3'b011 )
next_state=`EXEC_WRITE_ENTRY;
else
@ -340,7 +367,7 @@ always @( FLAGS or CIR or SIMPLE_MICRO or seq_addr_input ) begin
in_alu_sel2=2'b01;
reg_read_port2_addr={Wbit,3'b000};
OUT_MOD=3'b100;
ALU_1OP=`ALU_OP_SUB;
ALU_OP=`ALU_OP_SUB;
MEM_OR_IO=0;
if(Wbit==1)
next_state=`EXEC_DE_LOAD_16_PARAM;
@ -365,7 +392,7 @@ always @( FLAGS or CIR or SIMPLE_MICRO or seq_addr_input ) begin
in_alu_sel1=2'b10;
in_alu_sel2=2'b00;
PARAM2={{8{CIR[7:7]}},CIR[7:0]};
ALU_1OP=`ALU_OP_ADD_SIGNED_B;
ALU_OP=`ALU_OP_ADD_SIGNED_B;
MEM_OR_IO=0;
OUT_MOD=3'b101;
case(CIR[11:9])
@ -413,7 +440,7 @@ always @( FLAGS or CIR or SIMPLE_MICRO or seq_addr_input ) begin
in_alu_sel1=2'b10;
in_alu_sel2=2'b00;
PARAM2={{8{CIR[7:7]}},CIR[7:0]};
ALU_1OP=`ALU_OP_ADD_SIGNED_B;
ALU_OP=`ALU_OP_ADD_SIGNED_B;
OUT_MOD=3'b101;
MEM_OR_IO=0;
next_state=`EXEC_WRITE_ENTRY;
@ -484,7 +511,7 @@ always @( FLAGS or CIR or SIMPLE_MICRO or seq_addr_input ) begin
next_state=`EXEC_DE_LOAD_8_PARAM;
end
in_alu_sel1=2'b00; /* PARAM1 */
ALU_1OP=`ALU_OP_AND;
ALU_OP=`ALU_OP_AND;
case(IN_MOD)
3'b011:begin
in_alu_sel2=2'b01;
@ -527,7 +554,7 @@ always @( FLAGS or CIR or SIMPLE_MICRO or seq_addr_input ) begin
in_alu_sel2=2'b00;
next_state=`EXEC_MEMIO_READ;
end
ALU_1OP=`ALU_OP_ADD;
ALU_OP=`ALU_OP_ADD;
OUT_MOD=3'b101;
`normal_instruction;
memio_address_select=0;
@ -623,14 +650,14 @@ always @( FLAGS or CIR or SIMPLE_MICRO or seq_addr_input ) begin
/*1:1 map essentially but I want to keep the spec for these bits separate
* from the alu op select bits*/
case(ucode_data[22:20])
3'b000: ALU_1OP=`ALU_OP_ADD;
3'b001: ALU_1OP=`ALU_OP_SUB;
3'b010: ALU_1OP=`ALU_OP_AND;
3'b011: ALU_1OP=`ALU_OP_OR;
3'b100: ALU_1OP=`ALU_OP_XOR;
3'b101: ALU_1OP=`ALU_OP_ADD_SIGNED_B;
3'b110: ALU_1OP=`ALU_OP_SUB_REVERSE;
3'b111: ALU_1OP=`ALU_OP_SHIFT_LEFT;
3'b000: ALU_OP=`ALU_OP_ADD;
3'b001: ALU_OP=`ALU_OP_SUB;
3'b010: ALU_OP=`ALU_OP_AND;
3'b011: ALU_OP=`ALU_OP_OR;
3'b100: ALU_OP=`ALU_OP_XOR;
3'b101: ALU_OP=`ALU_OP_ADD_SIGNED_B;
3'b110: ALU_OP=`ALU_OP_SUB_REVERSE;
3'b111: ALU_OP=`ALU_OP_SHIFT_LEFT;
default: begin end
endcase
if(ucode_data[34:34]==0) /* Set reg read port 1 address */
@ -649,3 +676,54 @@ end
endmodule
// verilator lint_on BLKSEQ
/* IN: {CIR[15:8],CIR[5:3]} */
/* OUT: number in bytes */
module InstrSize ( input [10:0] IN, output reg [2:0] VERDICT );
always @( IN ) begin
casez(IN)
11'b0000_010?_??? : VERDICT <= 3'd2+{2'b0,IN[3:3]}; /* ADD - Add Immediate word/byte to accumulator */
11'b1000_00??_101 : VERDICT <= 3'd3+{2'b0,(IN[4:3]==2'b01)}; /* SUB - Subtract immediate word/byte from register/memory */
11'b1000_00??_000 : VERDICT <= 3'd3+{2'b0,(IN[4:3]==2'b01)}; /* ADD - Add Immediate word/byte to register/memory */
11'b1000_00??_111 : VERDICT <= 3'd3+{2'b0,(IN[4:3]==2'b01)}; /* CMP - compare Immediate with register / memory */
11'b1011_????_??? : VERDICT <= 3'd2+{2'b0,IN[6:6]}; /* MOV - Move Immediate byte to register */
11'b1000_10??_??? : VERDICT <= 3'd2; /* MOV - Reg/Mem to/from register */
11'b0100_????_??? : VERDICT <= 3'd1; /* DEC - Decrement Register | INC - Increment Register */
11'b1111_111?_00? : VERDICT <= 3'd2; /* INC - Register/Memory | DEC - Register/Memory */
11'b1111_0100_??? : VERDICT <= 3'd1; /* HLT - Halt */
11'b0011_110?_??? : VERDICT <= 3'd2+{2'b0,IN[3:3]}; /* CMP - Compare Immediate with accumulator */
11'b0111_????_??? : VERDICT <= 3'd2; /* Conditional relative jumps ( JE/JZ, JS/JNS ... ) */
11'b1110_1011_??? : VERDICT <= 3'd2; /* JMP - Unconditional jump direct within segment (short) */
11'b1110_1000_??? : VERDICT <= 3'd3; /* CALL - Direct call within segment */
11'b1100_0011_??? : VERDICT <= 3'd1; /* RET - Return from call within segment */
11'b1010_101?_??? : VERDICT <= 3'd1; /* STOS - Write byte/word to [DI] and increment accordingly */
11'b0101_0???_??? : VERDICT <= 3'd1; /* PUSH - SP-=2; [SP]=REG */
11'b1111_011?_000 : VERDICT <= 3'd3+{2'b0,IN[3:3]}; /* TEST - Bitwise AND affecting only flags */
11'b0101_1???_??? : VERDICT <= 3'd1; /* POP - REG=[SP]; SP+=2 */
11'b1111_1111_100 : VERDICT <= 3'd2; /* JMP - Unconditional indirect within segment jump */
11'b1100_011?_000 : VERDICT <= 3'd3+{2'b0,IN[3:3]}; /* MOV - Move immediate to register/memory */
11'b1100_1101_??? : VERDICT <= 3'd2; /* INT - execute interrupt handler */
11'b1110_011?_??? : VERDICT <= 3'd2; /* OUT - write AL or AX to a defined output port */
11'b1100_1111_??? : VERDICT <= 3'd1; /* IRET - Return from interrupt */
default:begin end
endcase
end
endmodule
`ifdef EARLY_VALID_INSTRUCTION
module Is1 ( input [7:0] IN, output reg VERDICT );
always @( IN ) begin
casez(IN)
8'b0100_???? : VERDICT <= 1; /* DEC - Decrement Register | INC - Increment Register */
8'b1111_0100 : VERDICT <= 1; /* HLT - Halt */
8'b1100_0011 : VERDICT <= 1; /* RET - Return from call within segment */
8'b1010_101? : VERDICT <= 1; /* STOS - Write byte/word to [DI] and increment accordingly */
8'b0101_0??? : VERDICT <= 1; /* PUSH - SP-=2; [SP]=REG */
8'b0101_1??? : VERDICT <= 1; /* POP - REG=[SP]; SP+=2 */
8'b1100_1111 : VERDICT <= 1; /* IRET - Return from interrupt */
default:begin VERDICT<= 0; end
endcase
end
endmodule
`endif

View File

@ -1,3 +1,22 @@
/* execute.v - Implements the instruction execution logic
This file is part of the 9086 project.
Copyright (c) 2023 Efthymios Kritikos
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
module execute_unit (
/* GENERAL */ input clock, input reset ,input Wbit, input Sbit, input opcode_size,input [23:0] INSTRUCTION_BUFFER,input valid_input
/* */ ,input [2:0] IN_MOD, input [2:0] OUT_MOD, input memio_address_select, input [15:0] ProgCount, input [2:0] RM, output reg [`ERROR_BITS-1:0] ERROR , input write /*TODO: REMOVE!!*/
@ -13,12 +32,11 @@ module execute_unit (
assign _exec_state_ = exec_state;
assign _ALU_O_ = ALU_O;
/*############ ALU / Execution units ########################################################## */
// ALU 1
reg [`EXEC_STATE_BITS-1:0] exec_state;
reg [15:0] PARAM1,PARAM2;
/*############ ALU / Execution units ################################################## */
mux4 #(.WIDTH(16)) MUX16_1A(
/*0*/ PARAM1,
/*1*/ reg_read_port1_data,
@ -41,28 +59,24 @@ wire [15:0] ALU_O;
wire [7:0] ALU_FLAGS;
ALU ALU1(
.A(ALU_A),
.B(ALU_B),
.OUT(ALU_O),
.op(ALU_OP),
.FLAGS(ALU_FLAGS),
.Wbit(Wbit)
);
/* INPUT 1 */ .A(ALU_A),
/* INPUT 2 */ .B(ALU_B),
/* OUTPUT */ .OUT(ALU_O),
/* OPERATION */ .op(ALU_OP),
/* FLAGS */ .FLAGS(ALU_FLAGS),
/* Wbit */ .Wbit(Wbit)
);
//reg valid_input_;
/*############ Execute logic ########################################################## */
always @(posedge valid_input) begin
exec_state <= init_state;
//valid_input_ <= 1;
end
always @(negedge set_initial_values) begin
PARAM1 <= PARAM1_INIT;
PARAM2 <= PARAM2_INIT;
end
//always @(negedge valid_input) begin
// valid_input_ <= 0;
//end
always @(negedge reset) begin
exec_state <= `EXEC_HALT;
@ -76,7 +90,6 @@ end
always @(posedge clock) begin
case (exec_state)
`EXEC_RESET: begin
//valid_input_ <= 0;
biu_write_request <= 0;
biu_read_request <= 0;
biu_data_direction <= 0;
@ -87,7 +100,6 @@ always @(posedge clock) begin
end
`EXEC_DONE:begin
reg_write_we <= 1;
//valid_input_ <= 0;
biu_jump_req <= 0;
use_exec_reg_addr <= 0;
end

View File

@ -23,54 +23,61 @@
`include "ucode_header.v"
`include "error_header.v"
//HALT: active high
//IOMEM: 1=IO 0=MEM
//write: active low
//read: active low
//reset: active low
`define PROC_STATE_BITS 3
`define PROC_RESET 3'b000
`define PROC_DE_STATE_ENTRY 3'b001
`define PROC_WAIT 3'b010
`define PROC_HALT 3'b011
module processor ( input clock, input reset, output [19:0] external_address_bus, inout [15:0] external_data_bus,output read, output write,output BHE,output IOMEM, output reg HALT,output [`ERROR_BITS-1:0] ERROR);
//HALT: active high
//IOMEM: 1=IO 0=MEM
//BHE: active low
//write: active low
//read: active low
//reset: active low
module processor ( input clock, input reset, output [19:0] external_address_bus, inout [15:0] external_data_bus,output read, output write,output BHE,output IOMEM, output wire HALT,output [`ERROR_BITS-1:0] ERROR);
/* If there is an error either from the decoder or execution unit set it to ERROR */
assign ERROR=(DE_ERROR_sampled!=`ERR_NO_ERROR)?DE_ERROR_sampled:(EXEC_ERROR!=`ERR_NO_ERROR)?EXEC_ERROR:`ERR_NO_ERROR;
assign ERROR=(DE_ERROR!=`ERR_NO_ERROR)?DE_ERROR:(EXEC_ERROR!=`ERR_NO_ERROR)?EXEC_ERROR:`ERR_NO_ERROR;
/*** Global Definitions ***/
reg [`PROC_STATE_BITS-1:0] state;
/*############ Execution Unit ################################################## */
reg [1:0] in_alu_sel1;
reg [1:0] in_alu_sel2;
/*############ Execution Unit ################################################### */
wire [1:0] in_alu_sel1, in_alu_sel2;
assign in_alu_sel1 = DE_OUTPUT_sampled[44:43];
assign in_alu_sel2 = DE_OUTPUT_sampled[46:45];
wire [`EXEC_STATE_BITS-1:0] exec_state;
reg valid_exec_data;
reg valid_exec_data, set_initial_values;
wire [`ERROR_BITS-1:0] EXEC_ERROR;
wire use_exec_reg_addr;
wire [3:0] EXEC_reg_read_port1_addr;
reg [2:0] IN_MOD,OUT_MOD;
reg [`EXEC_STATE_BITS-1:0] exec_state_init;
reg [`ALU_OP_BITS-1:0] ALU_OP;
wire [15:0] ALU_O;
wire [7:0]EXEC_FLAGS;
reg [15:0] PARAM1_INIT;
reg [15:0] PARAM2_INIT;
reg set_initial_values;
wire [15:0] PARAM1_INIT, PARAM2_INIT;
assign PARAM1_INIT = DE_OUTPUT_sampled[23:8];
assign PARAM2_INIT = DE_OUTPUT_sampled[39:24];
wire [2:0] IN_MOD,OUT_MOD;
assign IN_MOD=DE_OUTPUT_sampled[2:0];
assign OUT_MOD=DE_OUTPUT_sampled[49:47];
wire [`ALU_OP_BITS-1:0] ALU_OP;
assign ALU_OP = DE_OUTPUT_sampled[42:40];
execute_unit execute_unit (
/* GENERAL */ clock, reset, Wbit, Sbit, opcode_size, INSTRUCTION_BUFFER,valid_exec_data
/* */ ,IN_MOD, OUT_MOD,memio_address_select, ProgCount, RM, EXEC_ERROR, write
/* */ ,set_initial_values
/* PARAM */ ,PARAM1_INIT,PARAM2_INIT
/* STATE CONTROL */ ,exec_state, exec_state_init
/* STATE CONTROL */ ,exec_state, next_state
/* ALU CONTROL */ ,in_alu_sel1, in_alu_sel2, ALU_OP, ALU_O
/* REGISTER DATA */ ,reg_read_port1_data, reg_read_port2_data, EXEC_reg_read_port1_addr, use_exec_reg_addr, reg_write_we
/* FLAFS */ ,EXEC_FLAGS
@ -79,138 +86,96 @@ execute_unit execute_unit (
/*############ Bus Interface Unit ############################################### */
wire [31:0] INSTRUCTION;
wire biu_jump_req;
wire VALID_INSTRUCTION;
wire [15:0] INSTRUCTION_LOCATION;
wire [15:0] BIU_ADDRESS_INPUT;
wire [15:0] INSTRUCTION_LOCATION, BIU_ADDRESS_INPUT;
wire [15:0] BIU_DATA;
wire biu_write_request;
wire biu_data_direction;
wire biu_read_request;
wire BIU_VALID_DATA;
wire [31:0] INSTRUCTION;
wire biu_write_request, biu_read_request, BIU_VALID_DATA;
wire biu_jump_req, biu_data_direction,VALID_INSTRUCTION;
BIU BIU(
clock,reset,external_address_bus,external_data_bus,read,write,BHE,IOMEM,
INSTRUCTION,VALID_INSTRUCTION,INSTRUCTION_LOCATION,biu_jump_req,BIU_ADDRESS_INPUT,BIU_DATA,biu_write_request,biu_read_request,Wbit,BIU_VALID_DATA,MEM_OR_IO,
state,SIMPLE_MICRO
);
/* Outside world */ clock,reset,external_address_bus
/* */ ,external_data_bus,read,write,BHE,IOMEM
/* Internal */ ,INSTRUCTION,VALID_INSTRUCTION,INSTRUCTION_LOCATION,biu_jump_req
/* */ ,BIU_ADDRESS_INPUT,BIU_DATA,biu_write_request,biu_read_request,Wbit,BIU_VALID_DATA,MEM_OR_IO
/* */ ,state,SIMPLE_MICRO
);
assign BIU_DATA= biu_data_direction ? 16'hz : (memio_address_select ? reg_read_port1_data : ALU_O);
/*############ Decoder ########################################################## */
reg Wbit, Sbit, opcode_size;
wire DE_Wbit, DE_Sbit, DE_opcode_size;
wire [`EXEC_STATE_BITS-1:0] next_state;
reg [2:0]RM;
wire [15:0]DE_PARAM1;// Input param1 form decoder to alu
wire [15:0]DE_PARAM2;
wire [2:0]DE_IN_MOD;
wire [2:0]DE_RM;
wire [2:0]DE_OUT_MOD;
wire [`ERROR_BITS-1:0] DE_ERROR;
wire DE_HALT;
wire [3:0]DE_reg_read_port1_addr,DE_reg_write_addr,DE_reg_read_port2_addr;
wire [11:0]DE_REGISTER_CONTROL;
wire [2:0]INSTRUCTION_INFO;
wire [`ERROR_BITS:0]DECODER_SIGNALS;
wire [`UCODE_ADDR_BITS-1:0] ucode_seq_addr_entry;
reg SIMPLE_MICRO; /* output simple decodings (=0) or microcode data (=1) */
//TODO : remove completely?
reg memio_address_select;
wire DE_memio_address_select;
wire DE_MEM_OR_IO;
reg MEM_OR_IO;
wire [1:0] DE_in_alu_sel1;
wire [1:0] DE_in_alu_sel2;
reg [`ALU_OP_BITS-1:0] DE_ALU_OP;
decoder decoder(
.CIR(INSTRUCTION[31:16]),
.FLAGS(FLAGS),
.INSTRUCTION_INFO(INSTRUCTION_INFO),
.DECODER_SIGNALS(DECODER_SIGNALS),
.next_state(next_state),
.IN_MOD(DE_IN_MOD),
.RM(DE_RM),
.PARAM1(DE_PARAM1),
.PARAM2(DE_PARAM2),
.in_alu_sel1(DE_in_alu_sel1),
.in_alu_sel2(DE_in_alu_sel2),
.OUT_MOD(DE_OUT_MOD),
.REGISTER_FILE_CONTROL(DE_REGISTER_CONTROL),
.ALU_1OP(DE_ALU_OP),
.seq_addr_entry(ucode_seq_addr_entry),
.SIMPLE_MICRO(SIMPLE_MICRO),
.seq_addr_input(ucode_seq_addr),
.memio_address_select(DE_memio_address_select),
.MEM_OR_IO(DE_MEM_OR_IO)
);
assign DE_Wbit=INSTRUCTION_INFO[2:2];
assign DE_Sbit=INSTRUCTION_INFO[1:1];
assign DE_opcode_size=INSTRUCTION_INFO[0:0];
assign DE_reg_write_addr=DE_REGISTER_CONTROL[11:8];
assign DE_reg_read_port1_addr=DE_REGISTER_CONTROL[7:4];
assign DE_reg_read_port2_addr=DE_REGISTER_CONTROL[3:0];
assign DE_HALT=DECODER_SIGNALS[0:0];
assign DE_ERROR=DECODER_SIGNALS[`ERROR_BITS:1];
reg [`ERROR_BITS-1:0] DE_ERROR_sampled;
reg [`UCODE_ADDR_BITS-1:0] ucode_seq_addr;
reg SIMPLE_MICRO; /* output simple decodings (=0) or microcode data (=1) */
/*############ REGISTERS ########################################################## */
wire [`EXEC_STATE_BITS+`ERROR_BITS+65:0] DE_OUTPUT;
reg [`EXEC_STATE_BITS+`ERROR_BITS+65:0] DE_OUTPUT_sampled;
decoder decoder(
/* INPUTS */ INSTRUCTION[31:16],FLAGS,
/* MICROCODE */ ucode_seq_addr_entry,SIMPLE_MICRO,ucode_seq_addr,
/* OUTPUT */ DE_OUTPUT
);
wire [2:0] RM;
assign RM = DE_OUTPUT_sampled[5:3];
wire memio_address_select;
assign memio_address_select=DE_OUTPUT_sampled[6:6];
wire [3:0] DE_reg_read_port1_addr,DE_reg_read_port2_addr;
assign DE_reg_read_port1_addr=DE_OUTPUT_sampled[53:50];
assign DE_reg_read_port2_addr=DE_OUTPUT_sampled[57:54];
wire [3:0] reg_write_addr;
assign reg_write_addr=DE_OUTPUT_sampled[61:58];
wire MEM_OR_IO;
assign MEM_OR_IO = DE_OUTPUT_sampled[7:7];
wire Wbit, Sbit, opcode_size;
assign opcode_size=DE_OUTPUT_sampled[62:62];
assign Sbit=DE_OUTPUT_sampled[63:63];
assign Wbit=DE_OUTPUT_sampled[64:64];
wire [`ERROR_BITS-1:0] DE_ERROR;
assign HALT = DE_OUTPUT_sampled[65:65];
assign DE_ERROR = DE_OUTPUT_sampled[`ERROR_BITS+65:66];
wire [`EXEC_STATE_BITS-1:0] next_state;
assign next_state=DE_OUTPUT_sampled[`EXEC_STATE_BITS+`ERROR_BITS+65:`ERROR_BITS+66];
/*############ Registers ######################################################## */
// verilator lint_off UNDRIVEN
reg [15:0] FLAGS;
// verilator lint_on UNDRIVEN
//Architectural Register file
reg [3:0] reg_write_addr;
wire [15:0] reg_write_data;
wire reg_write_we;
wire [3:0] reg_read_port1_addr;
reg [15:0] reg_read_port1_data;
reg [3:0] reg_read_port2_addr;
reg [15:0] reg_read_port2_data;
reg [1:0] reg_write_in_sel;
mux4 #(.WIDTH(16)) REG_FILE_WRITE_IN_MUX(
ALU_O,
16'hz,
16'hz,
16'hz,
reg_write_in_sel,
reg_write_data);
register_file register_file(
.write_port1_addr(reg_write_addr),
.write_port1_data(reg_write_data),
.write_port1_we(reg_write_we),
.read_port1_addr(reg_read_port1_addr),
.read_port1_data(reg_read_port1_data),
.read_port2_addr(reg_read_port2_addr),
.read_port2_data(reg_read_port2_data)
);
assign reg_read_port1_addr = use_exec_reg_addr ? EXEC_reg_read_port1_addr : reg_read_port1_addr_latched;
reg [15:0] ProgCount;
/*############ Processor state machine ########################################################## */
reg [3:0] reg_read_port1_addr_latched;
wire [3:0] reg_read_port1_addr;
assign reg_read_port1_addr = use_exec_reg_addr ? EXEC_reg_read_port1_addr : DE_reg_read_port1_addr;
wire [15:0] reg_read_port1_data, reg_read_port2_data;
wire reg_write_we;
register_file register_file(
/* WRITE */ .write_port1_addr(reg_write_addr),
/* */ .write_port1_data(ALU_O),
/* */ .write_port1_we(reg_write_we),
/* READ 1 */ .read_port1_addr(reg_read_port1_addr),
/* */ .read_port1_data(reg_read_port1_data),
/* READ 2 */ .read_port2_addr(DE_reg_read_port2_addr),
/* */ .read_port2_data(reg_read_port2_data)
);
/*############ Processor State Machine ########################################## */
/*** RESET LOGIC ***/
/* verilator lint_off MULTIDRIVEN */
always @(negedge reset) begin
state <= `PROC_HALT; //TODO: race condition ??
end
always @(posedge reset) begin
state <= `PROC_RESET;
end
/* verilator lint_on MULTIDRIVEN */
/*** Processor stages ***/
@ -219,49 +184,28 @@ InstrSize InstrSize({INSTRUCTION[31:24],INSTRUCTION[21:19]},instr_end);
reg [23:0] INSTRUCTION_BUFFER;
always @(posedge clock) begin
case(state)
`PROC_RESET:begin
ucode_seq_addr <= `UCODE_NO_INSTRUCTION;
HALT <= 0;
DE_OUTPUT_sampled <= 0;
SIMPLE_MICRO <= 0;
state <= `PROC_DE_STATE_ENTRY;
reg_write_in_sel <= 2'b00; //only got wirtten in IF
end
`PROC_DE_STATE_ENTRY:begin
if(VALID_INSTRUCTION==1) begin
DE_OUTPUT_sampled <= DE_OUTPUT;
if(SIMPLE_MICRO==0)begin
/* We cannot set these directly within
* microcode so don't overwrite useful values
* each tie the next microcode is executed.
* Note this still allows to set initial values
* at the start of the microcode */
PARAM1_INIT <= DE_PARAM1;
PARAM2_INIT <= DE_PARAM2;
set_initial_values<=0;
`ifdef DEBUG_PC_ADDRESS
$display("Running command at %04x (%08x)",INSTRUCTION_LOCATION,INSTRUCTION);
`endif
ProgCount <= INSTRUCTION_LOCATION+{12'b0,instr_end};
INSTRUCTION_BUFFER<=INSTRUCTION[23:0];
set_initial_values<=0;
end
DE_ERROR_sampled <= DE_ERROR;
IN_MOD <= DE_IN_MOD;
OUT_MOD <= DE_OUT_MOD;
RM <= DE_RM;
HALT <= DE_HALT;
Wbit <= DE_Wbit;
Sbit <= DE_Sbit;
opcode_size <= DE_opcode_size;
memio_address_select<=DE_memio_address_select;
reg_read_port1_addr_latched <= DE_reg_read_port1_addr;
reg_read_port2_addr <= DE_reg_read_port2_addr;
reg_write_addr <= DE_reg_write_addr;
MEM_OR_IO <= DE_MEM_OR_IO;
in_alu_sel1 <= DE_in_alu_sel1;
in_alu_sel2 <= DE_in_alu_sel2;
ALU_OP <= DE_ALU_OP;
if ( (ucode_seq_addr==`UCODE_NO_INSTRUCTION) && (ucode_seq_addr_entry!=`UCODE_NO_INSTRUCTION) )begin
/*switch to microcode decoding*/
ucode_seq_addr <= ucode_seq_addr_entry;
@ -269,7 +213,6 @@ always @(posedge clock) begin
/*keep state the same and rerun decode this time with all the data from the microcode rom*/
end else begin
valid_exec_data <= 1;
exec_state_init <= next_state;
state <= `PROC_WAIT;
end
end
@ -277,10 +220,10 @@ always @(posedge clock) begin
`PROC_WAIT:begin
set_initial_values<=1;
if( exec_state == `EXEC_DONE ) begin
FLAGS[7:0] <= EXEC_FLAGS; //don't set all of them all the time!
FLAGS <= {8'b0,EXEC_FLAGS}; //TODO: don't set all of them all the time!
valid_exec_data<=0;
state <= `PROC_DE_STATE_ENTRY;
if(SIMPLE_MICRO == 1 ) begin
if( SIMPLE_MICRO == 1 ) begin
ucode_seq_addr <= ucode_seq_addr_entry; /*Reused for next address*/
if( ucode_seq_addr_entry == `UCODE_NO_INSTRUCTION )begin
/*Finished microcode*/
@ -296,5 +239,4 @@ always @(posedge clock) begin
endcase
end
endmodule