306 lines
11 KiB
Coq
306 lines
11 KiB
Coq
|
/* 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_NEXT 4'b0111
|
||
|
`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 [1:0] NEXT_POSITION,
|
||
|
/* */ 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
|
||
|
);
|
||
|
|
||
|
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;
|
||
|
|
||
|
reg [7:0] INPUT_FIFO [15:0]; //8bit fifo memory with 4bit address bus
|
||
|
|
||
|
reg [3:0] FIFO_start; /*inclusive*/
|
||
|
reg [3:0] FIFO_end; /*exclusive*/
|
||
|
|
||
|
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;
|
||
|
|
||
|
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 ) begin
|
||
|
/* verilator lint_off BLKSEQ */
|
||
|
FIFO_start = 4'b0;
|
||
|
/* verilator lint_on BLKSEQ */
|
||
|
FIFO_end <= 4'b0;
|
||
|
INSTRUCTION_ADDRESS <= { 4'b0 , ADDRESS_INPUT-16'd1 };
|
||
|
INSTRUCTION_LOCATION <= ADDRESS_INPUT;
|
||
|
func <= 1;
|
||
|
jump_req <= 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 ;
|
||
|
VALID_INSTRUCTION <= 0;
|
||
|
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 };
|
||
|
VALID_INSTRUCTION <= 0;
|
||
|
DATA_DIR <= 0;
|
||
|
read <=0;
|
||
|
biu_state <= (ADDRESS_INPUT[0:0])?`BIU_GET_UNALIGNED_DATA:`BIU_GET_ALIGNED_DATA;
|
||
|
end else begin
|
||
|
if((FIFO_end-FIFO_start)>4'b0011)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
|
||
|
if ( FIFO_end[3:0]+4'b0001 != FIFO_start[3:0] ) begin
|
||
|
func<=1;
|
||
|
biu_state <= `BIU_READ;
|
||
|
INSTRUCTION_ADDRESS <= INSTRUCTION_ADDRESS+1;
|
||
|
write <= 1;
|
||
|
read <= 0;
|
||
|
IOMEM <= 0;
|
||
|
BHE <= 0;
|
||
|
end else begin
|
||
|
biu_state <= `BIU_NEXT_ACTION;
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
/*************** INSTRUCTION FIFO READ ***************/
|
||
|
`BIU_READ: begin
|
||
|
/* verilator lint_off BLKSEQ */
|
||
|
if(INSTRUCTION_ADDRESS[0:0]==0)begin
|
||
|
INPUT_FIFO[FIFO_end] = external_data_bus[7:0];
|
||
|
end else begin
|
||
|
INPUT_FIFO[FIFO_end] = external_data_bus[15:8];
|
||
|
end
|
||
|
/* verilator lint_on BLKSEQ */
|
||
|
FIFO_end <= FIFO_end+1;
|
||
|
biu_state <= `BIU_NEXT_ACTION;
|
||
|
read<=1;
|
||
|
end
|
||
|
|
||
|
|
||
|
/*************** DATA WRITE ***************/
|
||
|
//TODO TODO TODO flush fifo, self modifying code
|
||
|
`BIU_PUT_UNALIGNED_16BIT_DATA:begin
|
||
|
BHE <= 0;
|
||
|
data_bus_output_register <= {DATA[7:0],DATA[15:8]};
|
||
|
biu_state <= `BIU_PUT_UNALIGNED_PREP_NEXT;
|
||
|
end
|
||
|
`BIU_PUT_UNALIGNED_PREP_NEXT:begin
|
||
|
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
|
||
|
data_bus_output_register <= {DATA[15:8],DATA[7:0]};
|
||
|
biu_state <= `BIU_WRITE_EXIT;
|
||
|
end
|
||
|
`BIU_PUT_BYTE:begin
|
||
|
biu_state <= `BIU_WRITE_EXIT;
|
||
|
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
|
||
|
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
|
||
|
DATA_OUT <= (Wbit==1)? external_data_bus : {8'b0,external_data_bus[7:0]} ;
|
||
|
read <=1;
|
||
|
`finished_read
|
||
|
end
|
||
|
`BIU_GET_UNALIGNED_DATA:begin
|
||
|
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
|
||
|
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'h0FFEF;
|
||
|
INSTRUCTION_LOCATION <= 16'hFFF0;
|
||
|
VALID_INSTRUCTION <= 0;
|
||
|
VALID_DATA <= 0;
|
||
|
DATA_DIR <= 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);
|
||
|
|
||
|
always @( NEXT_POSITION ) begin
|
||
|
case(NEXT_POSITION)
|
||
|
2'b00:begin end /* no action */
|
||
|
2'b01:begin /* Next instruction */
|
||
|
/* verilator lint_off BLKSEQ */
|
||
|
FIFO_start = FIFO_start + {1'b0,Isize};
|
||
|
INSTRUCTION_LOCATION <= INSTRUCTION_LOCATION + {12'b0,Isize};;
|
||
|
/* verilator lint_on BLKSEQ */
|
||
|
if((FIFO_end-FIFO_start)>4'b0011)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 else begin
|
||
|
VALID_INSTRUCTION <= 0;
|
||
|
end
|
||
|
|
||
|
end
|
||
|
2'b10:begin /* Jump to specific location based on register */
|
||
|
jump_req <= 1;
|
||
|
VALID_INSTRUCTION <= 0;
|
||
|
end
|
||
|
2'b11:begin /* Jump to absolute location */
|
||
|
end
|
||
|
endcase
|
||
|
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
|