/* 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 . */ `include "config.v" `define BIU_HALT 4'b0000 `define BIU_NEXT_ACTION 4'b0001 `define BIU_READ 4'b0010 `define BIU_RESET1 4'b0011 `define BIU_RESET2 4'b1111 `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 /* */ ,input [15:0] external_data_bus_read,output [15:0] external_data_bus_write,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 /**************** 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, input [15:0] DATA_EX_WRITE /**************** OTUPUT TO EX *****************/ /* */ ,output [15:0] DATA_EX_READ `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_write=data_bus_output_register; //TODO: should we rename reg [15:0] DATA_OUT; assign DATA_EX_READ=DATA_OUT; //TODO should we ranme `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; reg sane; 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 ( reset == 0 ) begin biu_state <= `BIU_HALT; write <= 1; sane <= 0; biu_state <= `BIU_RESET1; FIFO_start <= `L1_CACHE_SIZE'b0; FIFO_end <= `L1_CACHE_SIZE'b0; end else if ( jump_req ) begin FIFO_start <= FIFO_end ; INSTRUCTION_ADDRESS <= { 4'b0 , ADDRESS_INPUT }; INSTRUCTION_LOCATION <= ADDRESS_INPUT; func <= 1; if (biu_state==`BIU_READ) biu_state <= `BIU_NEXT_ACTION; end else if(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}; 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 }; 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} ; FIFO_end<=FIFO_start; end else if ( read_request ) begin func<=0; DATA_ADDRESS <= { 4'b0 , ADDRESS_INPUT }; 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 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_read[7:0]; INPUT_FIFO[FIFO_end+`L1_CACHE_SIZE'd1] <= external_data_bus_read[15:8]; FIFO_end <= FIFO_end+`L1_CACHE_SIZE'd2; INSTRUCTION_ADDRESS <= INSTRUCTION_ADDRESS+20'd2; end else if(INSTRUCTION_ADDRESS[0:0]==0)begin INPUT_FIFO[FIFO_end] <= external_data_bus_read[7:0]; FIFO_end <= FIFO_end+`L1_CACHE_SIZE'd1; INSTRUCTION_ADDRESS <= INSTRUCTION_ADDRESS+20'd1; end else begin INPUT_FIFO[FIFO_end] <= external_data_bus_read[15:8]; FIFO_end <= FIFO_end+`L1_CACHE_SIZE'd1; 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_EX_WRITE,DATA_ADDRESS); `endif BHE <= 0; data_bus_output_register <= {DATA_EX_WRITE[7:0],DATA_EX_WRITE[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_EX_WRTIE,DATA_ADDRESS); `endif data_bus_output_register <= {DATA_EX_WRITE[15:8],DATA_EX_WRITE[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_EX_WRITE[7:0],DATA_ADDRESS); `endif if(ADDRESS_INPUT[0:0]==0) begin BHE <= 1; data_bus_output_register <= {8'b0,DATA_EX_WRITE[7:0]}; end else begin BHE <= 0; data_bus_output_register <= {DATA_EX_WRITE[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 \ 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_read,DATA_ADDRESS); else $display("Reading 8bit %02x from %04x",external_data_bus_read[7:0],DATA_ADDRESS); `endif DATA_OUT <= (Wbit==1)? external_data_bus_read : {8'b0,external_data_bus_read[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_read[15:8],DATA_ADDRESS); `endif DATA_OUT[7:0] <= external_data_bus_read[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_read[7:0],DATA_OUT[7:0]},DATA_ADDRESS-1);//read started a byte earlier `endif DATA_OUT[15:8] <= external_data_bus_read[7:0]; `finished_read read <=1; end /*************** HOUSE KEEPING ***************/ `BIU_RESET1: begin biu_state <= `BIU_RESET2; VALID_DATA <= 0; end `BIU_RESET2: 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_DATA <= 0; sane<=1; end default: begin biu_state <= `BIU_NEXT_ACTION;/*Should be unreachable*/ end endcase end /**** UPDATE VALID_INSTRUCTION ****/ if(jump_req==1)begin VALID_INSTRUCTION <= 0; end else if(sane==1) begin //if(VALID_INSTRUCTION == 1 ) begin // `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 else begin //$display("trig fifoIsize=%d %d/%d [%02x %02x]",fifoIsize,FIFO_start,FIFO_end,INPUT_FIFO[FIFO_start],INPUT_FIFO[FIFO_start+1]); `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 VALID_INSTRUCTION <= 0; `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 else VALID_INSTRUCTION <= 0; `endif //end 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 endmodule