9086/system/biu.v

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; \
if ( read_request == 0 ) begin \
biu_state <= `BIU_NEXT_ACTION;\
VALID_DATA <= 0;\
end else \
VALID_DATA <= 1;
`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