399 lines
13 KiB
Verilog
399 lines
13 KiB
Verilog
/* 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.
|
|
|
|
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/>. */
|
|
|
|
`include "config.v"
|
|
|
|
`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 (
|
|
|
|
/***************** GENERAL *****************/
|
|
/* */ input clock, input reset
|
|
|
|
/**************** OUTSIDE WORLD ****************/
|
|
/* */ ,output wire [19:0] external_address_bus
|
|
/* */ ,inout [15:0] external_data_bus,output reg read, output reg write,output reg BHE,output reg IOMEM
|
|
|
|
/**************** OUTPUT TO DE ****************/
|
|
/* */ ,output reg [31:0] INSTRUCTION, output reg VALID_INSTRUCTION, output reg [15:0] INSTRUCTION_LOCATION
|
|
/* */ ,output reg VALID_DATA, output reg DATA_DIR
|
|
|
|
/**************** INPUT FROM DE ****************/
|
|
,input Wbit, input MEM_OR_IO, input valid_instruction_ack
|
|
|
|
/**************** INPUT FROM EX ****************/
|
|
/* */ ,input jump_req, input write_request, input read_request
|
|
/* */ ,input[15:0] ADDRESS_INPUT
|
|
|
|
/************ BIDIRECTIONAL WITH EX ************/
|
|
/* */ ,inout [15:0] DATA
|
|
|
|
`ifdef OTUPUT_JSON_STATISTICS
|
|
/***************** STATISTICS *****************/
|
|
/* */ ,output wire [`L1_CACHE_SIZE-1:0] FIFO_SIZE_STAT, output wire VALID_INSTRUCTION_STAT
|
|
`endif
|
|
|
|
);
|
|
|
|
`ifdef OTUPUT_JSON_STATISTICS
|
|
assign FIFO_SIZE_STAT = FIFO_SIZE;
|
|
assign VALID_INSTRUCTION_STAT = ((Isit1==1) && (FIFO_SIZE!=0) && `EARLY_VALID_INSTRUCTION_) || ((fifoIsize==2) && (FIFO_SIZE > 1) && `EARLY_VALID_INSTRUCTION_) || ((fifoIsize==3) && (FIFO_SIZE > 2) && `EARLY_VALID_INSTRUCTION_) || (FIFO_SIZE>3);
|
|
`endif
|
|
|
|
reg [15:0] data_bus_output_register;
|
|
assign external_data_bus=read?data_bus_output_register:16'hz;
|
|
|
|
reg [15:0] DATA_OUT;
|
|
assign DATA=DATA_DIR ? 16'hz:DATA_OUT;
|
|
|
|
`define FIFO_SIZE_BYTES $rtoi($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;
|
|
write <= 1;
|
|
end
|
|
always @(posedge reset) begin
|
|
biu_state <= `BIU_RESET;
|
|
/* verilator lint_off BLKSEQ */
|
|
FIFO_start = `L1_CACHE_SIZE'b0;
|
|
FIFO_end = `L1_CACHE_SIZE'b0;
|
|
/* verilator lint_on BLKSEQ */
|
|
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 ;
|
|
|
|
always @(posedge clock) begin
|
|
if ( jump_req_latch ) begin
|
|
/* verilator lint_off BLKSEQ */
|
|
FIFO_start = 0;
|
|
FIFO_end = 0;
|
|
/* verilator lint_on BLKSEQ */
|
|
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) ;
|
|
INSTRUCTION_ADDRESS <= {4'b0,INSTRUCTION_LOCATION} ;
|
|
/* verilator lint_off BLKSEQ */
|
|
FIFO_end=FIFO_start;
|
|
/* verilator lint_on BLKSEQ */
|
|
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!={`L1_CACHE_SIZE{1'b1}} ) 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
|
|
|
|
`ifdef EARLY_VALID_INSTRUCTION
|
|
|
|
if(FIFO_start==FIFO_end) begin
|
|
/*TODO: I would use FIFO_SIZE==0 here or better yet add an else at the
|
|
end but since FIFO_start and FIFO_end are updated in a blocking
|
|
manner it seems that the assign statement updating FIFO_SIZE
|
|
doesn't work. PLEASE CLEAN UP THIS MESS */
|
|
VALID_INSTRUCTION <= 0;
|
|
end else if((Isit1==1) && (FIFO_SIZE!=0))begin
|
|
VALID_INSTRUCTION <= 1;
|
|
INSTRUCTION[31:24] <= INPUT_FIFO[FIFO_start];
|
|
end else if((fifoIsize==2) && (FIFO_SIZE > `L1_CACHE_SIZE'd1))begin
|
|
VALID_INSTRUCTION <= 1;
|
|
INSTRUCTION[31:24] <= INPUT_FIFO[FIFO_start];
|
|
INSTRUCTION[23:16] <= INPUT_FIFO[FIFO_start+`L1_CACHE_SIZE'd1];
|
|
end else if((fifoIsize==3) && (FIFO_SIZE > `L1_CACHE_SIZE'd2))begin
|
|
VALID_INSTRUCTION <= 1;
|
|
INSTRUCTION[31:24] <= INPUT_FIFO[FIFO_start];
|
|
INSTRUCTION[23:16] <= INPUT_FIFO[FIFO_start+`L1_CACHE_SIZE'd1];
|
|
INSTRUCTION[15: 8] <= INPUT_FIFO[FIFO_start+`L1_CACHE_SIZE'd2];
|
|
end else if(FIFO_SIZE > `L1_CACHE_SIZE'd3)begin
|
|
VALID_INSTRUCTION <= 1;
|
|
INSTRUCTION[31:24] <= INPUT_FIFO[FIFO_start];
|
|
INSTRUCTION[23:16] <= INPUT_FIFO[FIFO_start+`L1_CACHE_SIZE'd1];
|
|
INSTRUCTION[15: 8] <= INPUT_FIFO[FIFO_start+`L1_CACHE_SIZE'd2];
|
|
INSTRUCTION[ 7: 0] <= INPUT_FIFO[FIFO_start+`L1_CACHE_SIZE'd3];
|
|
end
|
|
`else
|
|
if(FIFO_start==FIFO_end) begin
|
|
/*TODO: Same as on the first statment on the other side of the `ifdef */
|
|
VALID_INSTRUCTION <= 0;
|
|
end else if(FIFO_SIZE>`L1_CACHE_SIZE'd3)begin
|
|
VALID_INSTRUCTION <= 1;
|
|
INSTRUCTION[31:24] <= INPUT_FIFO[FIFO_start];
|
|
INSTRUCTION[23:16] <= INPUT_FIFO[FIFO_start+`L1_CACHE_SIZE'd1];
|
|
INSTRUCTION[15: 8] <= INPUT_FIFO[FIFO_start+`L1_CACHE_SIZE'd2];
|
|
INSTRUCTION[ 7: 0] <= INPUT_FIFO[FIFO_start+`L1_CACHE_SIZE'd3];
|
|
end
|
|
`endif
|
|
|
|
|
|
end
|
|
|
|
/*************** INSTRUCTION FIFO READ ***************/
|
|
`BIU_READ: begin
|
|
if(INSTRUCTION_ADDRESS[0:0]==0 && FIFO_SIZE<{{(`L1_CACHE_SIZE-1){1'b1}},1'b0})begin
|
|
INPUT_FIFO[FIFO_end] <= external_data_bus[7:0];
|
|
INPUT_FIFO[FIFO_end+`L1_CACHE_SIZE'd1] <= external_data_bus[15:8];
|
|
/* verilator lint_off BLKSEQ */
|
|
FIFO_end = FIFO_end+`L1_CACHE_SIZE'd2;
|
|
/* verilator lint_on BLKSEQ */
|
|
INSTRUCTION_ADDRESS <= INSTRUCTION_ADDRESS+20'd2;
|
|
end else if(INSTRUCTION_ADDRESS[0:0]==0)begin
|
|
INPUT_FIFO[FIFO_end] <= external_data_bus[7:0];
|
|
/* verilator lint_off BLKSEQ */
|
|
FIFO_end = FIFO_end+`L1_CACHE_SIZE'd1;
|
|
/* verilator lint_on BLKSEQ */
|
|
INSTRUCTION_ADDRESS <= INSTRUCTION_ADDRESS+20'd1;
|
|
end else begin
|
|
INPUT_FIFO[FIFO_end] <= external_data_bus[15:8];
|
|
/* verilator lint_off BLKSEQ */
|
|
FIFO_end = FIFO_end+`L1_CACHE_SIZE'd1;
|
|
/* verilator lint_on BLKSEQ */
|
|
INSTRUCTION_ADDRESS <= INSTRUCTION_ADDRESS+20'd1;
|
|
end
|
|
biu_state <= `BIU_NEXT_ACTION;
|
|
read<=1;
|
|
end
|
|
|
|
/*************** DATA WRITE ***************/
|
|
//TODO TODO TODO flush fifo, self modifying code
|
|
`BIU_PUT_UNALIGNED_16BIT_DATA:begin
|
|
`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+20'd1;
|
|
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+20'd1;
|
|
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 = `L1_CACHE_SIZE'b0;
|
|
FIFO_end = `L1_CACHE_SIZE'b0;
|
|
/* verilator lint_on BLKSEQ */
|
|
biu_state <= `BIU_NEXT_ACTION;
|
|
INSTRUCTION_ADDRESS <= 20'h0FFF0;
|
|
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);
|
|
|
|
`ifdef INCLUDE_EARLY_CALC_CIRUIT
|
|
wire [2:0] fifoIsize;
|
|
wire Isit1;
|
|
InstrSize fifoInstrSize({INPUT_FIFO[FIFO_start][7:0],INPUT_FIFO[FIFO_start+`L1_CACHE_SIZE'd1][5:3]},fifoIsize);
|
|
Is1 Is1(INPUT_FIFO[FIFO_start][7:0],Isit1);
|
|
`endif
|
|
|
|
`ifdef DOUBLE_INSTRUCTION_LOAD
|
|
wire [2:0] fifoIsize2;
|
|
InstrSize fifoInstrSize2(
|
|
{ INPUT_FIFO[FIFO_start+{{`L1_CACHE_SIZE-3{1'b0}},fifoIsize}][7:0], INPUT_FIFO[FIFO_start+{{`L1_CACHE_SIZE-3{1'b0}},fifoIsize}+`L1_CACHE_SIZE'd1][5:3]}
|
|
,fifoIsize2
|
|
);
|
|
`endif
|
|
|
|
always @( valid_instruction_ack ) begin
|
|
/* verilator lint_off BLKSEQ */
|
|
FIFO_start = FIFO_start + {{`L1_CACHE_SIZE-3{1'b0}},Isize};
|
|
/* verilator lint_on BLKSEQ */
|
|
INSTRUCTION_LOCATION <= INSTRUCTION_LOCATION + {13'd0,Isize};
|
|
`ifdef DOUBLE_INSTRUCTION_LOAD
|
|
if(FIFO_SIZE>`L1_CACHE_SIZE'd3+{{`L1_CACHE_SIZE-3{1'b0}},Isize})begin
|
|
if((fifoIsize2==2) && (FIFO_SIZE > `L1_CACHE_SIZE'd1+{{`L1_CACHE_SIZE-3{1'b0}},fifoIsize}))begin
|
|
VALID_INSTRUCTION <= 1;
|
|
INSTRUCTION[31:24] <= INPUT_FIFO[FIFO_start];
|
|
INSTRUCTION[23:16] <= INPUT_FIFO[FIFO_start+`L1_CACHE_SIZE'd1];
|
|
end else if((fifoIsize2==3) && (FIFO_SIZE > `L1_CACHE_SIZE'd2+{{`L1_CACHE_SIZE-3{1'b0}},fifoIsize}))begin
|
|
VALID_INSTRUCTION <= 1;
|
|
INSTRUCTION[31:24] <= INPUT_FIFO[FIFO_start];
|
|
INSTRUCTION[23:16] <= INPUT_FIFO[FIFO_start+`L1_CACHE_SIZE'd1];
|
|
INSTRUCTION[15: 8] <= INPUT_FIFO[FIFO_start+`L1_CACHE_SIZE'd2];
|
|
end else if(FIFO_SIZE>`L1_CACHE_SIZE'd3+{{`L1_CACHE_SIZE-3{1'b0}},fifoIsize})begin
|
|
VALID_INSTRUCTION <= 1;
|
|
INSTRUCTION[31:24] <= INPUT_FIFO[FIFO_start];
|
|
INSTRUCTION[23:16] <= INPUT_FIFO[FIFO_start+`L1_CACHE_SIZE'd1];
|
|
INSTRUCTION[15: 8] <= INPUT_FIFO[FIFO_start+`L1_CACHE_SIZE'd2];
|
|
INSTRUCTION[ 7: 0] <= INPUT_FIFO[FIFO_start+`L1_CACHE_SIZE'd3];
|
|
end else
|
|
VALID_INSTRUCTION <= 0;
|
|
end else begin
|
|
VALID_INSTRUCTION <= 0;
|
|
end
|
|
`else
|
|
VALID_INSTRUCTION <= 0;
|
|
`endif
|
|
end
|
|
|
|
always @( posedge jump_req ) begin
|
|
jump_req_latch <= 1;
|
|
DATA_DIR <= 1;
|
|
VALID_INSTRUCTION <= 0;
|
|
end
|
|
|
|
endmodule
|