390 lines
14 KiB
Verilog
390 lines
14 KiB
Verilog
/* processor.v - implementation of the 9086 bus interface unit. The logic that
|
|
controls all external bus functions
|
|
|
|
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/>. */
|
|
|
|
//IOMEM: 1=IO 0=MEM
|
|
|
|
`define BIU_HALT 4'b0000
|
|
`define BIU_NEXT_ACTION 4'b0001
|
|
`define BIU_READ 4'b0010
|
|
`define BIU_RESET 4'b0011
|
|
|
|
`define BIU_PUT_BYTE 4'b0100
|
|
`define BIU_PUT_UNALIGNED_16BIT_DATA 4'b0101
|
|
`define BIU_PUT_ALIGNED_16BIT_DATA 4'b0110
|
|
`define BIU_PUT_UNALIGNED_PREP_NEXT2 4'b1000
|
|
`define BIU_WRITE_EXIT 4'b1001
|
|
`define BIU_WRITE_RELEASE 4'b1010
|
|
|
|
`define BIU_GET_ALIGNED_DATA 4'b1011
|
|
`define BIU_GET_UNALIGNED_DATA 4'b1100
|
|
`define BIU_GET_SECOND_BYTE 4'b1101
|
|
`define BIU_GET_SECOND_BYTE1 4'b1110
|
|
|
|
module BIU (
|
|
/*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,
|
|
/* */ 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
|
|
);
|
|
|
|
reg [15:0] data_bus_output_register;
|
|
assign external_data_bus=read?data_bus_output_register:16'hz;
|
|
|
|
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 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*/
|
|
wire [`L1_CACHE_SIZE-1:0] FIFO_SIZE = FIFO_end-FIFO_start;
|
|
|
|
reg [3:0] biu_state;
|
|
|
|
always @(negedge reset) begin
|
|
biu_state <= `BIU_HALT;
|
|
end
|
|
|
|
always @(posedge reset) begin
|
|
biu_state <= `BIU_RESET;
|
|
end
|
|
|
|
reg jump_req_latch;
|
|
|
|
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 */
|
|
FIFO_start = 0;
|
|
/* verilator lint_on BLKSEQ */
|
|
FIFO_end <= 0;
|
|
INSTRUCTION_ADDRESS <= { 4'b0 , ADDRESS_INPUT };
|
|
INSTRUCTION_LOCATION <= ADDRESS_INPUT;
|
|
func <= 1;
|
|
jump_req_latch <= 0;
|
|
if (biu_state==`BIU_READ)
|
|
biu_state <= `BIU_NEXT_ACTION;
|
|
end else begin
|
|
case(biu_state)
|
|
`BIU_HALT: begin
|
|
end
|
|
`BIU_NEXT_ACTION: begin /* decide if we can read, if we are full or if we need to do something else */
|
|
if (write_request) begin
|
|
func<=0;
|
|
DATA_ADDRESS <= { 4'b0 , ADDRESS_INPUT };
|
|
DATA_DIR <= 1 ;
|
|
IOMEM <= MEM_OR_IO;
|
|
biu_state <= (Wbit==0) ? `BIU_PUT_BYTE : (ADDRESS_INPUT[0:0]?`BIU_PUT_UNALIGNED_16BIT_DATA:`BIU_PUT_ALIGNED_16BIT_DATA) ;
|
|
end else if ( read_request ) begin
|
|
func<=0;
|
|
DATA_ADDRESS <= { 4'b0 , ADDRESS_INPUT };
|
|
DATA_DIR <= 0;
|
|
IOMEM <= MEM_OR_IO;
|
|
read <= 0;
|
|
BHE <= 0;
|
|
biu_state <= (ADDRESS_INPUT[0:0])?`BIU_GET_UNALIGNED_DATA:`BIU_GET_ALIGNED_DATA;
|
|
end else begin
|
|
if ( FIFO_SIZE!=4'hF ) begin
|
|
func<=1;
|
|
biu_state <= `BIU_READ;
|
|
write <= 1;
|
|
read <= 0;
|
|
IOMEM <= 0;
|
|
BHE <= 0;
|
|
end else begin
|
|
biu_state <= `BIU_NEXT_ACTION;
|
|
end
|
|
end
|
|
|
|
if((Isit1==1) && (FIFO_SIZE!=0) && `EARLY_VALID_INSTRUCTION_)begin
|
|
VALID_INSTRUCTION <= 1;
|
|
INSTRUCTION[31:24] <= INPUT_FIFO[FIFO_start];
|
|
end else if((fifoIsize==2) && (FIFO_SIZE > 1) && `EARLY_VALID_INSTRUCTION_)begin
|
|
VALID_INSTRUCTION <= 1;
|
|
INSTRUCTION[31:24] <= INPUT_FIFO[FIFO_start];
|
|
INSTRUCTION[23:16] <= INPUT_FIFO[FIFO_start+4'd1];
|
|
end else if((fifoIsize==3) && (FIFO_SIZE > 2) && `EARLY_VALID_INSTRUCTION_)begin
|
|
VALID_INSTRUCTION <= 1;
|
|
INSTRUCTION[31:24] <= INPUT_FIFO[FIFO_start];
|
|
INSTRUCTION[23:16] <= INPUT_FIFO[FIFO_start+4'd1];
|
|
INSTRUCTION[15: 8] <= INPUT_FIFO[FIFO_start+4'd2];
|
|
end else if(FIFO_SIZE>3)begin
|
|
VALID_INSTRUCTION <= 1;
|
|
INSTRUCTION[31:24] <= INPUT_FIFO[FIFO_start];
|
|
INSTRUCTION[23:16] <= INPUT_FIFO[FIFO_start+4'd1];
|
|
INSTRUCTION[15: 8] <= INPUT_FIFO[FIFO_start+4'd2];
|
|
INSTRUCTION[ 7: 0] <= INPUT_FIFO[FIFO_start+4'd3];
|
|
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];
|
|
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];
|
|
FIFO_end <= FIFO_end+4'd1;
|
|
INSTRUCTION_ADDRESS <= INSTRUCTION_ADDRESS+1;
|
|
end else begin
|
|
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
|
|
BHE <= 0;
|
|
data_bus_output_register <= {DATA[7:0],DATA[15:8]};
|
|
write <= 0;
|
|
biu_state <= `BIU_PUT_UNALIGNED_PREP_NEXT2;
|
|
end
|
|
`BIU_PUT_UNALIGNED_PREP_NEXT2:begin
|
|
write <= 1;
|
|
DATA_ADDRESS <= DATA_ADDRESS+1;
|
|
BHE <= 1;
|
|
biu_state <= `BIU_WRITE_EXIT;
|
|
end
|
|
`BIU_PUT_ALIGNED_16BIT_DATA:begin
|
|
`ifdef DEBUG_DATA_READ_WRITES
|
|
$display("Writing 16bit %04x at %04x",DATA,DATA_ADDRESS);
|
|
`endif
|
|
data_bus_output_register <= {DATA[15:8],DATA[7:0]};
|
|
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);
|
|
`endif
|
|
if(ADDRESS_INPUT[0:0]==0) begin
|
|
BHE <= 1;
|
|
data_bus_output_register <= {8'b0,DATA[7:0]};
|
|
end else begin
|
|
BHE <= 0;
|
|
data_bus_output_register <= {DATA[7:0],8'b0};
|
|
end
|
|
write <= 0;
|
|
biu_state <= `BIU_WRITE_RELEASE;
|
|
end
|
|
`BIU_WRITE_EXIT:begin
|
|
write <= 0;
|
|
biu_state <= `BIU_WRITE_RELEASE;
|
|
end
|
|
`BIU_WRITE_RELEASE:begin
|
|
write <= 1;
|
|
biu_state <= `BIU_NEXT_ACTION;
|
|
end
|
|
|
|
|
|
/*************** DATA READ ***************/
|
|
`define finished_read \
|
|
DATA_DIR <= 0; \
|
|
VALID_DATA <= 1;\
|
|
if ( read_request == 0 ) begin \
|
|
biu_state <= `BIU_NEXT_ACTION;\
|
|
VALID_DATA <= 0;\
|
|
end
|
|
`BIU_GET_ALIGNED_DATA:begin
|
|
`ifdef DEBUG_DATA_READ_WRITES
|
|
if(Wbit==1)
|
|
$display("Reading 16bit %04x from %04x",external_data_bus,DATA_ADDRESS);
|
|
else
|
|
$display("Reading 8bit %02x from %04x",external_data_bus[7:0],DATA_ADDRESS);
|
|
`endif
|
|
DATA_OUT <= (Wbit==1)? external_data_bus : {8'b0,external_data_bus[7:0]} ;
|
|
read <=1;
|
|
`finished_read
|
|
end
|
|
`BIU_GET_UNALIGNED_DATA:begin
|
|
`ifdef DEBUG_DATA_READ_WRITES
|
|
if(Wbit==0)
|
|
$display("Reading 8bit %02x from %04x",external_data_bus[15:8],DATA_ADDRESS);
|
|
`endif
|
|
DATA_OUT[7:0] <= external_data_bus[15:8];
|
|
read <=1;
|
|
if(Wbit==1) begin
|
|
biu_state <= `BIU_GET_SECOND_BYTE;
|
|
end else begin
|
|
`finished_read
|
|
end
|
|
end
|
|
`BIU_GET_SECOND_BYTE:begin
|
|
DATA_ADDRESS <= DATA_ADDRESS+1;
|
|
biu_state <= `BIU_GET_SECOND_BYTE1;
|
|
read <=0;
|
|
end
|
|
`BIU_GET_SECOND_BYTE1:begin
|
|
`ifdef DEBUG_DATA_READ_WRITES
|
|
$display("Reading 16bit %02x from %04x",{external_data_bus[7:0],DATA_OUT[7:0]},DATA_ADDRESS-1);//read started a byte earlier
|
|
`endif
|
|
DATA_OUT[15:8] <= external_data_bus[7:0];
|
|
`finished_read
|
|
read <=1;
|
|
end
|
|
|
|
/*************** HOUSE KEEPING ***************/
|
|
`BIU_RESET: begin
|
|
/* verilator lint_off BLKSEQ */
|
|
FIFO_start = 4'b0;
|
|
/* verilator lint_on BLKSEQ */
|
|
FIFO_end <= 4'b0;
|
|
biu_state <= `BIU_NEXT_ACTION;
|
|
INSTRUCTION_ADDRESS <= 20'h0FFF0;
|
|
INSTRUCTION_LOCATION <= 16'hFFF0;
|
|
VALID_INSTRUCTION <= 0;
|
|
VALID_DATA <= 0;
|
|
DATA_DIR <= 0;
|
|
was_dec <= 0;
|
|
end
|
|
default: begin
|
|
biu_state <= `BIU_NEXT_ACTION;/*Should be unreachable*/
|
|
end
|
|
endcase
|
|
end
|
|
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);
|
|
`endif
|
|
|
|
reg was_dec;
|
|
reg was_simple;
|
|
|
|
always @( proc_state ) begin
|
|
case (proc_state)
|
|
`PROC_DE_STATE_ENTRY: begin
|
|
was_dec<=1;
|
|
end
|
|
default: begin
|
|
if( SIMPLE_MICRO==0 && was_dec==1 )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 */
|
|
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 */
|
|
VALID_INSTRUCTION <= 0;
|
|
end
|
|
end
|
|
endcase
|
|
end
|
|
|
|
always @( negedge SIMPLE_MICRO ) begin
|
|
was_simple <= 1;
|
|
end
|
|
|
|
always @( posedge jump_req ) begin
|
|
jump_req_latch <= 1;
|
|
VALID_INSTRUCTION <= 0;
|
|
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
|