/* 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_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 valid_instruction_ack `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; reg DATA_DIR; 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; 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; /* 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 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+20'd1; end else begin INPUT_FIFO[FIFO_end] <= external_data_bus[15:8]; FIFO_end <= FIFO_end+4'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,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 = 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; 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); wire [2:0] fifoIsize; wire Isit1; `ifdef EARLY_VALID_INSTRUCTION InstrSize fifoInstrSize({INPUT_FIFO[FIFO_start][7:0],INPUT_FIFO[FIFO_start+4'd1][5:3]},fifoIsize); Is1 Is1(INPUT_FIFO[FIFO_start][7:0],Isit1); `endif always @( valid_instruction_ack ) begin /* verilator lint_off BLKSEQ */ FIFO_start = FIFO_start + {1'b0,Isize}; /* verilator lint_on BLKSEQ */ INSTRUCTION_LOCATION <= INSTRUCTION_LOCATION + {12'b0,Isize};; VALID_INSTRUCTION <= 0; end always @( posedge jump_req ) begin jump_req_latch <= 1; VALID_INSTRUCTION <= 0; end endmodule