From f60084344e544c440fe1eefbfa855f8926a9110f Mon Sep 17 00:00:00 2001 From: "(Tim) Efthimis Kritikos" Date: Fri, 3 Mar 2023 06:29:06 +0000 Subject: [PATCH] Overhauled cpu frontend. Made memory byte addressable (necessary), cleaned up state machine and fixed small bug with MOV --- 8086_documentation.md | 18 ++ system/boot_code.asm | 9 +- system/decoder.v | 76 ++++---- system/memory.v | 11 +- system/proc_state_def.v | 11 +- system/processor.v | 377 +++++++++++++++++++++------------------- system/testbench.v | 5 +- 7 files changed, 281 insertions(+), 226 deletions(-) diff --git a/8086_documentation.md b/8086_documentation.md index b60528b..1165ce7 100644 --- a/8086_documentation.md +++ b/8086_documentation.md @@ -73,3 +73,21 @@ Flag register: * D - Direction flag : 1: string instructions decrement 0: they increment * O - Overflow flag : set on arithmetic overflow + +### Memory addressing +Memory is split into two 8bit banks and can be addresses separately with the A0 line and BHE pin + +| BHE | A0 | OPERATION | +|:-----:|:----:|-------------------------------| +| 0 | 0 |Both bytes addresses | +| 0 | 1 |Only higher 15:8 bits addressed| +| 1 | 0 |Only lower 7:0 bits addressed | +| 1 | 1 |No bits are addressed | + +Essentially address bits 19:1 address a 16bit memory while bits A0 and BHE select what byte(s) will be read or written + +|A1,A2| HBE=0 A0=1 | HBE=1 A0=0 | +|-----|:----------:|:----------:| +| 0,0 | B1 | B0 | +| 0,1 | B3 | B2 | +| 1,0 | B5 | B4 | diff --git a/system/boot_code.asm b/system/boot_code.asm index b70d357..94a21f1 100644 --- a/system/boot_code.asm +++ b/system/boot_code.asm @@ -1,8 +1,9 @@ start: +MOV SP,#STACK MOV AX,#0x0000 MOV CX,#0x0000 MOV BX,#0x0000 -ADD AX,#0xDEAD +CALL TEST_ ADD CX,#0xBEEF ADD CX,#0x4111 mov AX,#0x00FF @@ -30,3 +31,9 @@ mov ah,#2 mov dl,#'0 int #0x21 hlt +TEST_: +ADD AX,#0xDEAD +RET + + .BLKB 10 +STACK: diff --git a/system/decoder.v b/system/decoder.v index faa23ec..2949cae 100644 --- a/system/decoder.v +++ b/system/decoder.v @@ -43,12 +43,13 @@ assign DATA=ucode[ADDR]; endmodule module decoder( - input wire [15:0] CIR,input wire [15:0] FLAGS, output wire [4:0] INSTRUCTION_INFO, output wire [1:0]DECODER_SIGNALS,output reg [`PROC_STATE_BITS-1:0]next_state + input wire [15:0] CIR,input wire [15:0] FLAGS, output wire [3:0] INSTRUCTION_INFO, output wire [1:0]DECODER_SIGNALS,output reg [`PROC_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_alu1_sel1,output reg [1:0]in_alu1_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 [2:0]instruction_size ); reg [3:0]reg_read_port1_addr; @@ -57,8 +58,8 @@ reg [3:0]reg_write_addr; assign REGISTER_FILE_CONTROL={reg_write_addr,reg_read_port1_addr,reg_read_port2_addr}; /* For correct fetching of instructions and global options for the alu */ -reg Wbit,Sbit,unaligning,opcode_size,has_operands; -assign INSTRUCTION_INFO={Wbit,Sbit,unaligning,opcode_size,has_operands}; +reg Wbit,Sbit,unaligning,opcode_size; +assign INSTRUCTION_INFO={Wbit,Sbit,unaligning,opcode_size}; reg ERROR, HALT; assign DECODER_SIGNALS={ERROR,HALT}; @@ -87,12 +88,14 @@ always @( CIR or SIMPLE_MICRO or seq_addr_input ) begin /* ADD - Add Immediate word/byte to accumulator */ /* 0 0 0 0 0 1 0 W | DATA | DATA if W |*/ opcode_size=0; - has_operands=1; Wbit=CIR[8:8]; - if(Wbit) + if(Wbit)begin `start_unaligning_instruction - else + instruction_size=3; + end else begin `start_aligning_instruction + instruction_size=2; + end IN_MOD=2'b11; in_alu1_sel1=2'b00; in_alu1_sel2=2'b01; @@ -112,7 +115,6 @@ always @( CIR or SIMPLE_MICRO or seq_addr_input ) begin /* SUB - Subtract immediate word/byte from register/memory */ /* 1 0 0 0 0 0 S W | MOD 1 0 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]; IN_MOD=CIR[7:6]; @@ -130,10 +132,12 @@ always @( CIR or SIMPLE_MICRO or seq_addr_input ) begin 2'b00,2'b11:begin `start_unaligning_instruction next_state=`PROC_DE_LOAD_8_PARAM; + instruction_size=3; end 2'b01:begin `start_aligning_instruction next_state=`PROC_DE_LOAD_16_PARAM; + instruction_size=4; end default:begin `invalid_instruction @@ -152,16 +156,17 @@ always @( CIR or SIMPLE_MICRO or seq_addr_input ) 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]; IN_MOD=CIR[7:6]; RM=CIR[2:0]; case({Sbit,Wbit}) 2'b00,2'b11:begin + instruction_size=3; `start_unaligning_instruction end 2'b01:begin + instruction_size=4; `start_aligning_instruction end 2'b10:begin @@ -187,8 +192,8 @@ always @( CIR or SIMPLE_MICRO or seq_addr_input ) begin /* MOV - Move Immediate byte to register */ /* 1 0 1 1 W REG | DATA | DATA if W |*/ `start_aligning_instruction - has_operands=1; Wbit=CIR[11:11]; /* IS 0 */ + instruction_size=2; opcode_size=0; IN_MOD=2'b11; in_alu1_sel1=2'b00; @@ -203,8 +208,8 @@ always @( CIR or SIMPLE_MICRO or seq_addr_input ) begin 11'b1011_1xxx_xxx : begin /*MOV - Move Immediate word to register*/ `start_unaligning_instruction - has_operands=1; Wbit=CIR[11:11]; /*IS 1 */ + instruction_size=3; opcode_size=0; IN_MOD=2'b11; in_alu1_sel1=2'b00; @@ -219,16 +224,16 @@ always @( CIR or SIMPLE_MICRO or seq_addr_input ) begin 11'b1000_10xx_xxx : begin /* MOV - Reg/Mem to/from register */ /* 1 0 0 0 1 0 D W | MOD REG RM | < DISP LO > | < DISP HI > |*/ - has_operands=0; `start_aligning_instruction opcode_size=1; - IN_MOD=CIR[7:6]; + instruction_size=2; RM=CIR[2:0]; Wbit=CIR[8:8]; in_alu1_sel1=2'b00; PARAM1=0; if(CIR[9:9] == 1)begin /* Mem/Reg to reg */ + IN_MOD={1'b0,CIR[7:6]}; if(IN_MOD==2'b11)begin /*Reg to Reg*/ in_alu1_sel2=2'b01; @@ -243,16 +248,16 @@ always @( CIR or SIMPLE_MICRO or seq_addr_input ) begin reg_write_addr={Wbit,CIR[5:3]}; end else begin /* Reg to Mem/Reg */ + IN_MOD=3'b011; + OUT_MOD={1'b0,CIR[7:6]}; if(IN_MOD==2'b11)begin /*Reg to Reg*/ in_alu1_sel2=2'b01; - OUT_MOD=3'b011; reg_write_addr={Wbit,RM}; next_state=`PROC_EX_STATE_ENTRY; end else begin /*Reg to Mem*/ in_alu1_sel2=2'b00; - OUT_MOD={1'b0,IN_MOD}; next_state=`PROC_DE_LOAD_REG_TO_PARAM; end reg_read_port2_addr={Wbit,CIR[5:3]}; @@ -265,7 +270,7 @@ always @( CIR or SIMPLE_MICRO or seq_addr_input ) begin /* | 0 1 0 0 1 REG | */ /* INC - Increment Register */ /* | 0 1 0 0 0 REG | */ - has_operands=0; + instruction_size=1; opcode_size=0; `start_unaligning_instruction Wbit=1; @@ -287,7 +292,7 @@ always @( CIR or SIMPLE_MICRO or seq_addr_input ) begin /* 1 1 1 1 1 1 1 W | MOD 0 0 0 R/M | < DISP LO> | < DISP HI> */ /* DEC - Register/Memory */ /* 1 1 1 1 1 1 1 W | MOD 0 0 1 R/M | < DISP LO> | < DISP HI> */ - has_operands=0; + instruction_size=2; opcode_size=1; `start_aligning_instruction Wbit=CIR[8:8]; @@ -311,7 +316,7 @@ always @( CIR or SIMPLE_MICRO or seq_addr_input ) begin 11'b1111_0100_xxx : begin /* HLT - Halt */ /* 1 1 1 1 0 1 0 0 | */ - has_operands=0; + instruction_size=1; opcode_size=0; `start_unaligning_instruction IN_MOD=2'b11; @@ -326,11 +331,13 @@ always @( CIR or SIMPLE_MICRO or seq_addr_input ) begin /* W flag and my assembler seem to disagree */ Wbit=CIR[8:8]; opcode_size=0; - has_operands=1; - if(Wbit) + if(Wbit)begin + instruction_size=3; `start_unaligning_instruction - else + end else begin + instruction_size=2; `start_aligning_instruction + end IN_MOD=2'b11; in_alu1_sel1=2'b00; in_alu1_sel2=2'b01; @@ -353,7 +360,7 @@ always @( CIR or SIMPLE_MICRO or seq_addr_input ) begin /* JNS -Jump on not Sign */ /* 0 1 1 1 1 0 0 1 | IP-INC8 |*/ /* .... */ - has_operands=1; + instruction_size=2; `start_aligning_instruction Wbit=1; opcode_size=0; @@ -401,8 +408,8 @@ always @( CIR or SIMPLE_MICRO or seq_addr_input ) begin /* JMP - Unconditional jump direct within segment (short) */ /* | 1 1 1 0 1 0 1 1 | IP-INC-LO | */ `start_aligning_instruction + instruction_size=2; opcode_size=0; - has_operands=1; Wbit=1; in_alu1_sel1=2'b10; in_alu1_sel2=2'b00; @@ -414,7 +421,7 @@ always @( CIR or SIMPLE_MICRO or seq_addr_input ) begin 11'b1100_1101_xxx:begin /* INT - execute interrupt handler */ /* 1 1 0 0 1 1 0 1 | DATA |*/ - has_operands=1; + instruction_size=2; opcode_size=0; `start_aligning_instruction /* Emulate MS-DOS print routines */ @@ -428,9 +435,9 @@ always @( CIR or SIMPLE_MICRO or seq_addr_input ) begin /* 1 1 1 0 1 0 0 0 | IP-INC-LO | IP-INC-HI |*/ // Microcode instruction + instruction_size=3; `start_unaligning_instruction opcode_size=0; - has_operands=1; Wbit=1; Sbit=1; PARAM2=2; //subtract from sp @@ -442,13 +449,8 @@ always @( CIR or SIMPLE_MICRO or seq_addr_input ) begin // Microcode instruction `start_unaligning_instruction + instruction_size=1; opcode_size=0; - /* TODO: This is a hack to prevent IF from - * thinking it can retrieve half of the opcode - * from CIR and the previous byte on the data - * bus. We are jumping so all that data has to - * be thrown away */ - has_operands=1; Wbit=1; Sbit=0; PARAM1=2; @@ -459,7 +461,7 @@ always @( CIR or SIMPLE_MICRO or seq_addr_input ) begin /* | 1 0 1 0 1 0 1 W | */ `start_unaligning_instruction opcode_size=0; - has_operands=0; + instruction_size=1; Wbit=CIR[8:8]; Sbit=0; RM=101; @@ -471,7 +473,7 @@ always @( CIR or SIMPLE_MICRO or seq_addr_input ) begin /* | 0 1 0 1 0 REG | */ `start_unaligning_instruction opcode_size=0; - has_operands=0; + instruction_size=1; Wbit=1; Sbit=0; PARAM2=2; @@ -482,14 +484,15 @@ always @( CIR or SIMPLE_MICRO or seq_addr_input ) begin /* TEST - Bitwise AND affecting only flags */ /* 1 1 1 1 0 1 1 W | MOD 0 0 0 R/M | < DISP-LO > | < DISP-HI > | DATA | DATA if W */ opcode_size=1; - has_operands=1; Wbit=CIR[8:8]; IN_MOD={1'b0,CIR[7:6]}; RM={CIR[2:0]}; if(Wbit==1)begin `start_aligning_instruction + instruction_size=4; next_state=`PROC_DE_LOAD_16_PARAM; end else begin + instruction_size=3; `start_unaligning_instruction next_state=`PROC_DE_LOAD_8_PARAM; end @@ -511,7 +514,7 @@ always @( CIR or SIMPLE_MICRO or seq_addr_input ) begin /* | 0 1 0 1 1 REG | */ `start_unaligning_instruction opcode_size=0; - has_operands=0; + instruction_size=1; Wbit=1; Sbit=0; PARAM1=2; @@ -523,7 +526,7 @@ always @( CIR or SIMPLE_MICRO or seq_addr_input ) begin /* 1 1 1 1 1 1 1 1 | MOD 1 0 0 R/M | < DISP-LO > | < DISP-HI > */ `start_aligning_instruction opcode_size=1; - has_operands=0; + instruction_size=2; Wbit=1; IN_MOD={1'b0,CIR[7:6]}; RM=CIR[2:0]; @@ -544,13 +547,14 @@ always @( CIR or SIMPLE_MICRO or seq_addr_input ) begin /* 1 1 0 0 0 1 1 W | MOD 0 0 0 R/M | < DISP-LO > | < DISP-HI > | DATA | DATA if W */ Wbit=CIR[8:8]; opcode_size=1; - has_operands=1; in_alu1_sel1=2'b00; in_alu1_sel2=2'b11; if(Wbit==1)begin + instruction_size=4; `start_aligning_instruction; next_state=`PROC_DE_LOAD_16_PARAM; end else begin + instruction_size=3; `start_unaligning_instruction; next_state=`PROC_DE_LOAD_8_PARAM; end diff --git a/system/memory.v b/system/memory.v index 1cee02a..56d4d8e 100644 --- a/system/memory.v +++ b/system/memory.v @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -module mem(input [19:0] address,inout wire [15:0] data ,input rd,input wr,input cs); +module doublemem(input [19:0] address,inout wire [15:0] data ,input rd,input wr,input BHE,input cs); reg [15:0] memory [0:32768]; initial begin string boot_code; @@ -28,10 +28,15 @@ initial begin $readmemh(boot_code, memory,0,16383); end -assign data = !rd & !cs ? memory[address[15:0]]: 16'hz; +assign data[7:0] = !address[0:0] & !rd & !cs ? memory[address[15:1]][15:8] : 16'hz; + +assign data[15:8] = !BHE & !rd & !cs ? memory[address[15:1]][7:0] : 16'hz; always @(negedge wr) begin - memory[address[15:0]]=data; + if(BHE==0) + memory[address[15:1]][7:0]=data[15:8]; + if(address[0]==0) + memory[address[15:1]][15:8]=data[7:0]; end endmodule diff --git a/system/proc_state_def.v b/system/proc_state_def.v index 354a4ea..ffda0ed 100644 --- a/system/proc_state_def.v +++ b/system/proc_state_def.v @@ -29,6 +29,7 @@ `define PROC_IF_STATE_EXTRA_FETCH_SET 6'b000011 `define PROC_IF_STATE_EXTRA_FETCH 6'b000100 + /*DECODE SATE*/ `define PROC_DE_STATE_ENTRY 6'b001000 `define PROC_DE_LOAD_16_PARAM 6'b001001 @@ -44,7 +45,6 @@ `define PROC_MEMIO_GET_SECOND_BYTE 6'b010100 `define PROC_MEMIO_GET_SECOND_BYTE1 6'b010101 `define PROC_DE_LOAD_8_PARAM 6'b010110 -`define PROC_DE_LOAD_8_PARAM_UNALIGNED 6'b010111 /*EXECUTE STATE*/ `define PROC_EX_STATE_ENTRY 6'b100000 @@ -52,16 +52,11 @@ /*MEM/IO WRITE*/ `define PROC_MEMIO_WRITE 6'b101000 //`define PROC_MEMIO_WRITE_SETADDR 6'b010101 -`define PROC_MEMIO_PUT_ALIGNED_DATA 6'b101001 -`define PROC_MEMIO_PUT_UNALIGNED_DATA 6'b101010 +`define PROC_MEMIO_PUT_ALIGNED_16BIT_DATA 6'b101001 +`define PROC_MEMIO_PUT_UNALIGNED_16BIT_DATA 6'b101010 `define PROC_MEMIO_PUT_BYTE 6'b101011 -`define PROC_MEMIO_PUT_BYTE_STOP_READ 6'b101100 `define PROC_MEMIO_WRITE_EXIT 6'b101101 -`define PROC_MEMIO_PUT_UNALIGNED_FIRST_BYTE 6'b101110 `define PROC_MEMIO_PUT_UNALIGNED_PREP_NEXT 6'b101111 -`define PROC_MEMIO_PUT_UNALIGNED_PREP_NEXT1 6'b110001 `define PROC_MEMIO_PUT_UNALIGNED_PREP_NEXT2 6'b110010 -`define PROC_MEMIO_PUT_UNALIGNED_PREP_NEXT3 6'b110011 -`define PROC_MEMIO_PUT_UNALIGNED_PREP_NEXT4 6'b110100 `define PROC_NEXT_MICROCODE 6'b111000 diff --git a/system/processor.v b/system/processor.v index 3e9db85..9e3eda4 100644 --- a/system/processor.v +++ b/system/processor.v @@ -22,7 +22,14 @@ `include "config.v" `include "ucode_header.v" -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 HALT,output reg ERROR); +//HALT: active high +//ERROR: active high +//IOMEM: 1=IO 0=MEM +//write: active low +//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); /*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; @@ -33,7 +40,7 @@ assign external_data_bus=read?data_bus_output_register:16'hz; reg [`PROC_STATE_BITS-1:0] state; /*############ Decoder ########################################################## */ -wire Wbit, Sbit, unaligning_instruction,opcode_size, has_operands; +wire Wbit, Sbit, unaligning_instruction,opcode_size; wire [`PROC_STATE_BITS-1:0] next_state; wire [2:0]RM; wire [15:0]DE_PARAM1;// Input param1 form decoder to alu @@ -41,26 +48,41 @@ wire [15:0]DE_PARAM2; wire DE_ERROR,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 [4:0]INSTRUCTION_INFO; +wire [3:0]INSTRUCTION_INFO; wire [1:0]DECODER_SIGNALS; wire [`UCODE_ADDR_BITS-1:0] ucode_seq_addr_entry; reg SIMPLE_MICRO; /* otuput simple decodings (=0) or microcode data (=1) */ +wire [2:0] DE_instruction_size; +reg instruction_size_init; +wire [2:0] instruction_size; +assign instruction_size = instruction_size_init ? 3'b010 : DE_instruction_size; decoder decoder( - CIR,FLAGS,INSTRUCTION_INFO,DECODER_SIGNALS,next_state - ,IN_MOD,RM,DE_PARAM1,DE_PARAM2 - ,in_alu1_sel1,in_alu1_sel2,OUT_MOD - ,DE_REGISTER_CONTROL - ,ALU_1OP - ,ucode_seq_addr_entry,SIMPLE_MICRO,ucode_seq_addr + .CIR(CIR), + .FLAGS(FLAGS), + .INSTRUCTION_INFO(INSTRUCTION_INFO), + .DECODER_SIGNALS(DECODER_SIGNALS), + .next_state(next_state), + .IN_MOD(IN_MOD), + .RM(RM), + .PARAM1(DE_PARAM1), + .PARAM2(DE_PARAM2), + .in_alu1_sel1(in_alu1_sel1), + .in_alu1_sel2(in_alu1_sel2), + .OUT_MOD(OUT_MOD), + .REGISTER_FILE_CONTROL(DE_REGISTER_CONTROL), + .ALU_1OP(ALU_1OP), + .seq_addr_entry(ucode_seq_addr_entry), + .SIMPLE_MICRO(SIMPLE_MICRO), + .seq_addr_input(ucode_seq_addr), + .instruction_size(DE_instruction_size) ); -assign Wbit=INSTRUCTION_INFO[4:4]; -assign Sbit=INSTRUCTION_INFO[3:3]; -assign unaligning_instruction=INSTRUCTION_INFO[2:2]; -assign opcode_size=INSTRUCTION_INFO[1:1]; -assign has_operands=INSTRUCTION_INFO[0:0]; +assign Wbit=INSTRUCTION_INFO[3:3]; +assign Sbit=INSTRUCTION_INFO[2:2]; +assign unaligning_instruction=INSTRUCTION_INFO[1:1]; +assign 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]; @@ -73,13 +95,9 @@ reg [`UCODE_ADDR_BITS-1:0] ucode_seq_addr; /*############ REGISTERS ########################################################## */ -reg [19:0] ProgCount; //TODO: do i create a lot of adders each place i increment it? reg [15:0] CIR; reg [15:0] PARAM1; reg [15:0] PARAM2; -reg one_byte_instruction; -reg unaligned_access; -reg we_jumped; /*Only used to signify that a microcoded instruction jumped and we should not update the unaligned_access bit after the end of the instruction*/ reg [15:0]FLAGS; @@ -101,7 +119,21 @@ mux4 #(.WIDTH(16)) REG_FILE_WRITE_IN_MUX( 16'hz, reg_write_in_sel, reg_write_data); -register_file register_file(reg_write_addr,reg_write_data,reg_write_we,reg_read_port1_addr,reg_read_port1_data,reg_read_port2_addr,reg_read_port2_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) +); + +reg [15:0] ProgCount; +wire ProgCount_next_opcode; +wire ProgCount_arg; +assign ProgCount_next_opcode=ProgCount+instruction_size; +assign ProgCount_arg=ProgCount+opcode_size+1; /*############ ALU / Execution units ########################################################## */ // ALU 1 @@ -114,7 +146,7 @@ reg [2:0] OUT_MOD; mux4 #(.WIDTH(16)) MUX16_1A( /*0*/ PARAM1, /*1*/ reg_read_port1_data, -/*2*/ {ProgCount[14:0],unaligned_access^unaligning_instruction}, +/*2*/ ProgCount[15:0], /*3*/ 16'b0000000000000000, /*0 Constant*/ in_alu1_sel1, ALU_1A); @@ -122,7 +154,7 @@ mux4 #(.WIDTH(16)) MUX16_1A( mux4 #(.WIDTH(16)) MUX16_1B( /*0*/ PARAM2, /*1*/ reg_read_port2_data, -/*2*/ {ProgCount[14:0],unaligned_access^unaligning_instruction}, +/*2*/ ProgCount[15:0], /*3*/ 16'b0000000000000000, /*0 Constant*/ in_alu1_sel2, ALU_1B); @@ -132,7 +164,14 @@ wire [15:0] ALU_1B; wire [15:0] ALU_1O; reg [`ALU_OP_BITS-1:0]ALU_1OP; wire [7:0] ALU_1FLAGS; -ALU ALU1(ALU_1A,ALU_1B,ALU_1O,ALU_1OP,ALU_1FLAGS,Wbit); +ALU ALU1( + .A(ALU_1A), + .B(ALU_1B), + .OUT(ALU_1O), + .op(ALU_1OP), + .FLAGS(ALU_1FLAGS), + .Wbit(Wbit) +); /*############ Processor state machine ########################################################## */ @@ -145,98 +184,19 @@ always @(negedge reset) begin ProgCount=0;//TODO: Reset Vector HALT=0; reg_write_we=1; - unaligned_access=0; + IOMEM=0; @(posedge reset) @(negedge clock); state=`PROC_IF_STATE_ENTRY; - one_byte_instruction=0; ERROR=0; SIMPLE_MICRO=0; + instruction_size_init=1; end end /*** Processor stages ***/ `define invalid_instruction state=`PROC_IF_STATE_ENTRY;ERROR=1; -always @(negedge clock) begin - case(state) - `PROC_IF_WRITE_CIR:begin - if(unaligned_access)begin - if(one_byte_instruction==1)begin /*TODO: have a read buffer so we can do this even with data reads */ - CIR <= {CIR[7:0],external_data_bus[15:8]}; - state=`PROC_DE_STATE_ENTRY; - end else begin - CIR[15:8] <= external_data_bus[7:0]; - state=`PROC_IF_STATE_EXTRA_FETCH_SET; - end - end else begin - CIR <= external_data_bus; - ProgCount=ProgCount+1; - state=`PROC_DE_STATE_ENTRY; - end - end - `PROC_IF_STATE_EXTRA_FETCH:begin - CIR[7:0] <= external_data_bus[15:8]; - state=`PROC_DE_STATE_ENTRY; - end - `PROC_DE_LOAD_16_EXTRA_FETCH_SET:begin - external_address_bus = ProgCount; - state=`PROC_DE_LOAD_16_EXTRA_FETCH; - end - `PROC_MEMIO_READ_SETADDR:begin - external_address_bus = {5'b00000,reg_read_port1_data[15:1]}; - state=reg_read_port1_data[0:0]?`PROC_MEMIO_GET_UNALIGNED_DATA:`PROC_MEMIO_GET_ALIGNED_DATA; - end - `PROC_MEMIO_PUT_BYTE:begin - BYTE_WRITE_TEMP_REG=external_data_bus; - state=`PROC_MEMIO_PUT_BYTE_STOP_READ; - end - `PROC_MEMIO_WRITE_EXIT:begin - write=0; - if (ucode_seq_addr==`UCODE_NO_INSTRUCTION) - state=`PROC_IF_STATE_ENTRY; - else - state=`PROC_NEXT_MICROCODE; - end - `PROC_MEMIO_PUT_ALIGNED_DATA:begin - read=1; - data_bus_output_register={ALU_1O[7:0],ALU_1O[15:8]}; - state=`PROC_MEMIO_WRITE_EXIT; - end - `PROC_MEMIO_PUT_UNALIGNED_DATA:begin - BYTE_WRITE_TEMP_REG=external_data_bus; - state=`PROC_MEMIO_PUT_UNALIGNED_FIRST_BYTE; - end - `PROC_MEMIO_PUT_UNALIGNED_PREP_NEXT:begin - write=0; - state=`PROC_MEMIO_PUT_UNALIGNED_PREP_NEXT2; - data_bus_output_register={BYTE_WRITE_TEMP_REG[15:8],ALU_1O[7:0]}; - end - `PROC_MEMIO_PUT_UNALIGNED_PREP_NEXT3:begin - BYTE_WRITE_TEMP_REG=external_data_bus; - state=`PROC_MEMIO_PUT_UNALIGNED_PREP_NEXT4; - end - `PROC_MEMIO_GET_SECOND_BYTE:begin - external_address_bus=external_address_bus+1; - state=`PROC_MEMIO_GET_SECOND_BYTE1; - end - `PROC_DE_LOAD_8_PARAM_UNALIGNED:begin - if({Sbit,Wbit}==2'b11)begin - PARAM1 = {{8{external_data_bus[15:15]}},external_data_bus[15:8]}; - end else begin - PARAM1[7:0] = external_data_bus[15:8]; - end - case(IN_MOD) - 3'b000,3'b001,3'b010: state=`PROC_MEMIO_READ; - default: state=`PROC_EX_STATE_ENTRY; - endcase - end - default:begin - end - endcase -end - - always @(posedge clock) begin case(state) `PROC_HALT_STATE:begin @@ -247,34 +207,71 @@ always @(posedge clock) begin * testbench stop the clock after ERROR gets * raised the logic for the rising edge still * gets triggered printing this debug message. */ - if(ERROR!=1) - $display("Fetched instruction at %04x",{ProgCount[18:0],unaligned_access}); + if(ERROR!=1)begin + if(instruction_size==1) + $display("Fetched instruction at %0x",ProgCount - 1); + else + $display("Fetched instruction at %0x",ProgCount - 0); + end `endif + BHE = 0; external_address_bus = ProgCount; read = 0; write = 1; reg_write_we=1; state=`PROC_IF_WRITE_CIR; reg_write_in_sel=2'b00; - we_jumped=0; + end + `PROC_IF_WRITE_CIR:begin + /*I built the entire decode stage with CIR + * being big endian so just convert it here*/ + + if(instruction_size==1)begin + /*Half on CIR half on this address */ + state=`PROC_DE_STATE_ENTRY; + if(ProgCount[0:0]==1)begin + CIR = {CIR[7:0],external_data_bus[15:8]}; + end else begin + CIR = {CIR[7:0],external_data_bus[7:0]}; + end + ProgCount=ProgCount+1; + end else begin + if(ProgCount[0:0]==1)begin + /* Half on this address half on the next*/ + ProgCount=ProgCount+1; + CIR[15:8] <= external_data_bus[15:8]; + state=`PROC_IF_STATE_EXTRA_FETCH_SET; + end else begin + /* Both on this address! */ + ProgCount=ProgCount+2; + CIR <= {external_data_bus[7:0],external_data_bus[15:8]}; + state=`PROC_DE_STATE_ENTRY; + end + end + end `PROC_IF_STATE_EXTRA_FETCH_SET:begin - ProgCount=ProgCount+1; external_address_bus = ProgCount; + BHE=0; state=`PROC_IF_STATE_EXTRA_FETCH; end + `PROC_IF_STATE_EXTRA_FETCH:begin + CIR[7:0] <= external_data_bus[7:0]; + ProgCount=ProgCount+1; + state=`PROC_DE_STATE_ENTRY; + end `PROC_DE_STATE_ENTRY:begin - /* If we are unaligned, the address bus contains the - * ProgCount and points to the second word containing - * the next unread byte in external_data_bus[7:0]. If - * we are aligned the address bus points to the first - * word of the instruction which contains no useful - * data anymore but the ProgCount has the correct - * address so update it now so that whatever the case - * external_data_bus contains at least some unknown data */ - one_byte_instruction=(!has_operands)&&(!opcode_size); external_address_bus = ProgCount; if(SIMPLE_MICRO==0)begin + /*This flag is set at reset and jump because + * at IF we need to know the size of the + * previous instruction (specificly if it was + * a single byte and the value would be + * incorrect in both cases. So when it gets + * set reset it only at the start of the next + * 8086 instruction */ + instruction_size_init=0; + /* We cannot set these directly within * microcode so don't overwrite useful values * each time the next microcode is executed. @@ -299,7 +296,10 @@ always @(posedge clock) begin end `PROC_DE_LOAD_REG_TO_PARAM:begin PARAM2=reg_read_port2_data; - state=`PROC_EX_STATE_ENTRY; + case(IN_MOD) + 3'b000,3'b001,3'b010: state=`PROC_MEMIO_READ; + default: state=`PROC_EX_STATE_ENTRY; + endcase end `PROC_DE_LOAD_8_PARAM:begin if(opcode_size==0)begin @@ -314,44 +314,49 @@ always @(posedge clock) begin default: state=`PROC_EX_STATE_ENTRY; endcase end else begin - if(unaligned_access==1)begin + if(ProgCount[0:0]==1)begin + if({Sbit,Wbit}==2'b11)begin + /*signed "16bit" read*/ + PARAM1 = {{8{external_data_bus[15:15]}},external_data_bus[15:8]}; + end else begin + PARAM1[7:0] = external_data_bus[15:8]; + end + end else begin if({Sbit,Wbit}==2'b11)begin /*signed "16bit" read*/ PARAM1 = {{8{external_data_bus[7:7]}},external_data_bus[7:0]}; end else begin PARAM1[7:0] = external_data_bus[7:0]; end - ProgCount=ProgCount+1; - case(IN_MOD) - 3'b000,3'b001,3'b010: state=`PROC_MEMIO_READ; - default: state=`PROC_EX_STATE_ENTRY; - endcase - end else begin - external_address_bus=ProgCount; - state=`PROC_DE_LOAD_8_PARAM_UNALIGNED; - end - end - end - `PROC_DE_LOAD_16_PARAM:begin - if(opcode_size==0)begin - if(unaligned_access==1)begin - PARAM1 = {external_data_bus[7:0],external_data_bus[15:8]}; - ProgCount=ProgCount+1; - end else begin - PARAM1 = {external_data_bus[15:8],CIR[7:0]}; end + ProgCount=ProgCount+1; case(IN_MOD) 3'b000,3'b001,3'b010: state=`PROC_MEMIO_READ; default: state=`PROC_EX_STATE_ENTRY; endcase - - end else begin + end + end + `PROC_DE_LOAD_16_PARAM:begin + if(opcode_size==0)begin + PARAM1[7:0] = CIR[7:0]; + if(ProgCount[0:0]==1)begin + PARAM1[15:8] = external_data_bus[15:8]; + end else begin + PARAM1[15:8] = external_data_bus[7:0]; + end ProgCount=ProgCount+1; - if(unaligned_access==1)begin - PARAM1[7:0] = external_data_bus[7:0]; + case(IN_MOD) + 3'b000,3'b001,3'b010: state=`PROC_MEMIO_READ; + default: state=`PROC_EX_STATE_ENTRY; + endcase + end else begin + if(ProgCount[0:0]==1)begin + ProgCount=ProgCount+1; + PARAM1[7:0] = external_data_bus[15:8]; state=`PROC_DE_LOAD_16_EXTRA_FETCH_SET; end else begin - PARAM1 = {external_data_bus[7:0],external_data_bus[15:8]}; + PARAM1 = external_data_bus; + ProgCount=ProgCount+2; case(IN_MOD) 3'b000,3'b001,3'b010: state=`PROC_MEMIO_READ; default: state=`PROC_EX_STATE_ENTRY; @@ -359,8 +364,13 @@ always @(posedge clock) begin end end end + `PROC_DE_LOAD_16_EXTRA_FETCH_SET:begin + external_address_bus = ProgCount; + state=`PROC_DE_LOAD_16_EXTRA_FETCH; + end `PROC_DE_LOAD_16_EXTRA_FETCH:begin - PARAM1[15:8] = external_data_bus[15:8]; + ProgCount=ProgCount+1; + PARAM1[15:8] = external_data_bus[7:0]; case(IN_MOD) 3'b000,3'b001,3'b010: state=`PROC_MEMIO_READ; default: state=`PROC_EX_STATE_ENTRY; @@ -422,27 +432,34 @@ always @(posedge clock) begin `invalid_instruction end endcase - + end + `PROC_MEMIO_READ_SETADDR:begin + external_address_bus = {5'b0000,reg_read_port1_data[15:0]}; + state=reg_read_port1_data[0:0]?`PROC_MEMIO_GET_UNALIGNED_DATA:`PROC_MEMIO_GET_ALIGNED_DATA; end `PROC_MEMIO_GET_ALIGNED_DATA:begin - PARAM2=(Wbit==1)? {external_data_bus[7:0],external_data_bus[15:8]} : {8'b00000000,external_data_bus[15:8]} ; + PARAM2=(Wbit==1)? external_data_bus : {8'b00000000,external_data_bus[7:0]} ; state=`PROC_EX_STATE_ENTRY; end `PROC_MEMIO_GET_UNALIGNED_DATA:begin - PARAM2={8'b00000000,external_data_bus[7:0]}; + PARAM2={8'b00000000,external_data_bus[15:8]}; if(Wbit==1) begin state=`PROC_MEMIO_GET_SECOND_BYTE; end else begin state=`PROC_EX_STATE_ENTRY; end end + `PROC_MEMIO_GET_SECOND_BYTE:begin + external_address_bus=external_address_bus+1; + state=`PROC_MEMIO_GET_SECOND_BYTE1; + end + `PROC_MEMIO_GET_SECOND_BYTE1:begin + PARAM2[15:8]=external_data_bus[7:0]; + state=`PROC_EX_STATE_ENTRY; + end `PROC_EX_STATE_ENTRY:begin - FLAGS[7:0] = ALU_1FLAGS[7:0]; //TODO, we should probably move all the ...STATE_EXIT stuff here - /*Don't update the unaligned_access for Instruction - * Fetch if we are doing microcode execution, it will - * be done by decode at the end*/ - if (ucode_seq_addr==`UCODE_NO_INSTRUCTION) - unaligned_access=unaligning_instruction^unaligned_access; + external_address_bus = ProgCount; + FLAGS[7:0] = ALU_1FLAGS[7:0]; case(OUT_MOD) 3'b000, 3'b001, @@ -499,9 +516,8 @@ always @(posedge clock) begin state=`PROC_NEXT_MICROCODE; end 3'b101:begin /* Program Counter*/ - ProgCount={5'b00000,ALU_1O[15:1]}; - unaligned_access=ALU_1O[0:0]; - we_jumped=1; + ProgCount={5'b0000,ALU_1O[15:0]}; + instruction_size_init=1; if (ucode_seq_addr==`UCODE_NO_INSTRUCTION) state=`PROC_IF_STATE_ENTRY; else @@ -521,45 +537,54 @@ always @(posedge clock) begin `ifdef DEBUG_MEMORY_WRITES $display("Writing at %04x , %04x",reg_read_port1_data,ALU_1O); `endif - external_address_bus = {5'b00000,reg_read_port1_data[15:1]}; - state = (Wbit==0) ? `PROC_MEMIO_PUT_BYTE : (reg_read_port1_data[0:0]?`PROC_MEMIO_PUT_UNALIGNED_DATA:`PROC_MEMIO_PUT_ALIGNED_DATA) ; + external_address_bus = {5'b0000,reg_read_port1_data[15:0]}; + state = (Wbit==0) ? `PROC_MEMIO_PUT_BYTE : (reg_read_port1_data[0:0]?`PROC_MEMIO_PUT_UNALIGNED_16BIT_DATA:`PROC_MEMIO_PUT_ALIGNED_16BIT_DATA) ; end - `PROC_MEMIO_PUT_BYTE_STOP_READ:begin - read=1; - state=`PROC_MEMIO_WRITE_EXIT; - if(reg_read_port1_data[0:0]==0) - data_bus_output_register={ALU_1O[7:0],BYTE_WRITE_TEMP_REG[7:0]}; - else - data_bus_output_register={BYTE_WRITE_TEMP_REG[15:8],ALU_1O[7:0]}; - end - `PROC_MEMIO_PUT_UNALIGNED_FIRST_BYTE:begin + `PROC_MEMIO_PUT_UNALIGNED_16BIT_DATA:begin read=1; + BHE=0; + data_bus_output_register={ALU_1O[7:0],ALU_1O[15:8]}; state=`PROC_MEMIO_PUT_UNALIGNED_PREP_NEXT; - data_bus_output_register={BYTE_WRITE_TEMP_REG[15:8],ALU_1O[7:0]}; + end + `PROC_MEMIO_PUT_UNALIGNED_PREP_NEXT:begin + write=0; + state=`PROC_MEMIO_PUT_UNALIGNED_PREP_NEXT2; end `PROC_MEMIO_PUT_UNALIGNED_PREP_NEXT2:begin - external_address_bus=external_address_bus+1; write=1; - read=0; - state=`PROC_MEMIO_PUT_UNALIGNED_PREP_NEXT3; + external_address_bus=external_address_bus+1; + BHE=1; + state=`PROC_MEMIO_WRITE_EXIT; end - `PROC_MEMIO_PUT_UNALIGNED_PREP_NEXT4:begin + `PROC_MEMIO_PUT_ALIGNED_16BIT_DATA:begin + read=1; + data_bus_output_register={ALU_1O[15:8],ALU_1O[7:0]}; + state=`PROC_MEMIO_WRITE_EXIT; + end + `PROC_MEMIO_PUT_BYTE:begin read=1; state=`PROC_MEMIO_WRITE_EXIT; - data_bus_output_register={ALU_1O[15:8],BYTE_WRITE_TEMP_REG[7:0]}; + if(reg_read_port1_data[0:0]==0) begin + BHE=1; + data_bus_output_register={8'b0,ALU_1O[7:0]}; + end else begin + data_bus_output_register={ALU_1O[7:0],8'b0}; + end end - `PROC_MEMIO_GET_SECOND_BYTE1:begin - PARAM2[15:8]=external_data_bus[15:8]; - state=`PROC_EX_STATE_ENTRY; + `PROC_MEMIO_WRITE_EXIT:begin + write=0; + if (ucode_seq_addr==`UCODE_NO_INSTRUCTION) + state=`PROC_IF_STATE_ENTRY; + else + state=`PROC_NEXT_MICROCODE; end `PROC_NEXT_MICROCODE:begin read=0; write=1; // maybe we are coming from MEMIO_WRITE + BHE=0; ucode_seq_addr=ucode_seq_addr_entry; /*Reused for next address*/ if( ucode_seq_addr == `UCODE_NO_INSTRUCTION )begin /*Finished microcode*/ - if(we_jumped==0) - unaligned_access=unaligning_instruction^unaligned_access; SIMPLE_MICRO=0; state=`PROC_IF_STATE_ENTRY; end else begin diff --git a/system/testbench.v b/system/testbench.v index 6483d1d..6271307 100644 --- a/system/testbench.v +++ b/system/testbench.v @@ -27,9 +27,10 @@ wire [19:0]address_bus; wire [15:0]data_bus; wire rd,wr,romcs,HALT; wire ERROR; +wire IOMEM; -processor p(clock,reset,address_bus,data_bus,rd,wr,HALT,ERROR); -mem sysmem(address_bus,data_bus,rd,wr,romcs); +processor p(clock,reset,address_bus,data_bus,rd,wr,BHE,IOMEM,HALT,ERROR); +doublemem sysmem(address_bus,data_bus,rd,wr,BHE,IOMEM); `define CPU_SPEED 1000