1
0
mirror of https://github.com/Gehstock/Mist_FPGA.git synced 2026-04-25 03:54:57 +00:00
Files
Gehstock.Mist_FPGA/common/CPU/68K30L/wf68k30L_bus_interface.vhd
2020-09-06 18:16:38 +02:00

880 lines
43 KiB
VHDL

------------------------------------------------------------------------
---- ----
---- WF68K30L IP Core: this is the bus interface. ----
---- ----
---- Description: ----
---- This module is a 68030 compatible bus controller featuring ----
---- all of the 68030 bus interface functionality. ----
---- ----
---- Bus cycle operation: ----
---- A bus cycle is invoked by either asserting RD_REQ, WR_REQ or ----
---- OPCODE_REQ. Data is provided immediately or after the ----
---- respective bus cycle has finished. The RD_REQ, WR_REQ or ----
---- OPCODE_REQ signals should stay asserted until the respective ----
---- _RDY signal from the bus controller indicates, that the data ----
---- is available. These _RDY signals are strobes. If more than one ----
---- read or write request are asserted the same time, RD_REQ is ----
---- prioritized over WR_REQ and OPCODE_REQ has lowest priority. ----
---- For more information of the signal functionality of the bus ----
---- controller entity see also the comments below. ----
---- ----
---- Remarks: ----
---- ----
---- Bus arbitration topics: ----
---- Additionally to the single wire and the three wire bus arbi- ----
---- tration as described in the 68030 hardware manual, the bus ----
---- controller also features the two wire arbitration as des- ----
---- cribed in the documentation of the 68020 processor. ----
---- ----
---- Author(s): ----
---- - Wolfgang Foerster, wf@experiment-s.de; wf@inventronik.de ----
---- ----
------------------------------------------------------------------------
---- ----
---- Copyright © 2014-2019 Wolfgang Foerster Inventronik GmbH. ----
---- ----
---- This documentation describes Open Hardware and is licensed ----
---- under the CERN OHL v. 1.2. You may redistribute and modify ----
---- this documentation under the terms of the CERN OHL v.1.2. ----
---- (http://ohwr.org/cernohl). This documentation is distributed ----
---- WITHOUT ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING OF ----
---- MERCHANTABILITY, SATISFACTORY QUALITY AND FITNESS FOR A ----
---- PARTICULAR PURPOSE. Please see the CERN OHL v.1.2 for ----
---- applicable conditions ----
---- ----
------------------------------------------------------------------------
--
-- Revision History
--
-- Revision 2K14B 20141201 WF
-- Initial Release.
-- Revision 2K18A 20180620 WF
-- Suppress bus faults during RESET instruction.
-- Optimized ASn and DSn timing for synchronous RAM.
-- DATA_PORT_EN timing optimization.
-- BUS_EN is now active except during arbitration.
-- Rearanged the DATA_RDY vs. BUS_FLT logic.
-- Opted out START_READ and CHK_RD.
-- Fixed the faulty bus arbitration logic.
-- Rearranged address error handling.
-- Revision 2K20A 20200620 WF
-- ASn and DSn are not asserted in S0 any more.
-- Some modifications to optimize the RETRY logic.
-- Fixed a bug in the DSACK_MEM logic (now switches explicitely to "00").
--
library work;
use work.WF68K30L_PKG.all;
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;
entity WF68K30L_BUS_INTERFACE is
port (
-- System control:
CLK : in std_logic; -- System clock.
-- Adress bus:
ADR_IN_P : in std_logic_vector(31 downto 0); -- Logical address line inputs.
ADR_OUT_P : out std_logic_vector(31 downto 0); -- Address line outputs.
-- Function code relevant stuff:
FC_IN : in std_logic_vector(2 downto 0); -- Function codes.
FC_OUT : out std_logic_vector(2 downto 0); -- Physical function codes (top level entity).
-- Data bus:
DATA_PORT_IN : in std_logic_vector(31 downto 0); -- Data bus input lines (top level entity).
DATA_PORT_OUT : out std_logic_vector(31 downto 0); -- Data bus output lines (top level entity).
DATA_FROM_CORE : in std_logic_vector(31 downto 0); -- Internal bus input lines.
DATA_TO_CORE : out std_logic_vector(31 downto 0); -- Internal data bus output lines.
OPCODE_TO_CORE : out std_logic_vector(15 downto 0); -- Internal instruction bus output lines.
-- Tri state controls:
DATA_PORT_EN : out std_logic; -- For the data bus.
BUS_EN : out std_logic; -- For all other bus control signals.
-- Transfer and operation size:
SIZE : out std_logic_vector(1 downto 0); -- This is the size information (top level entity).
OP_SIZE : in OP_SIZETYPE; -- Used for bus access control.
-- Control signals:
RD_REQ : in bit; -- Read data.
WR_REQ : in bit; -- Write data.
DATA_RDY : out bit; -- Indicates 'new data available' (this is a strobe).
DATA_VALID : out std_logic; -- The data buffer contains valid data when '1'.
OPCODE_REQ : in bit; -- Read opcode.
OPCODE_RDY : out bit; -- Indicates 'new opcode available' (this is a strobe).
OPCODE_VALID : out std_logic; -- The opcode buffer contains valid data when '1'.
RMC : in bit; -- Indicates a read modify write operation.
BUSY_EXH : in bit;
INBUFFER : out std_logic_vector(31 downto 0); -- Used by the exception handler for stack frame type B.
OUTBUFFER : out std_logic_vector(31 downto 0); -- Used by the exception handler for stack frame types A and B.
SSW_80 : out std_logic_vector(8 downto 0);
-- Asynchronous bus control signals:
DSACKn : in std_logic_vector(1 downto 0); -- Asynchronous bus cycle termination (top level entity).
ASn : out std_logic; -- Adress select (top level entity).
DSn : out std_logic; -- Data select (top level entity).
RWn : out std_logic; -- Hi is read, low = write (top level entity).
RMCn : out std_logic; -- Read modify write indicator (top level entity).
ECSn : out std_logic; -- External cycle start (top level entity).
OCSn : out std_logic; -- Operand cycle start (top level entity).
DBENn : out std_logic; -- Data buffer enable (top level entity).
-- Synchronous bus control:
STERMn : in std_logic; -- Synchronous bus cycle termination (top level entity).
-- Bus arbitration:
BRn : in std_logic; -- Bus request (top level entity).
BGACKn : in std_logic; -- Bus grant acknowledge (top level entity).
BGn : out std_logic; -- Bus grant (top level entity).
-- Exception signals:
RESET_IN : in std_logic; -- System's reset input (top level entity).
RESET_STRB : in bit; -- From Core: force external reset.
RESET_OUT : out std_logic; -- System's reset output open drain enable.
RESET_CPU : out bit; -- Internal reset used for CPU initialization.
AVECn : in std_logic; -- Auto interrupt vector input (top level entity).
HALTn : in std_logic; -- Halt (top level entity).
BERRn : in std_logic; -- Bus error (top level entity).
AERR : buffer bit; -- Core internal address error.
BUS_BSY : out bit -- Bus is busy when '1'.
);
end entity WF68K30L_BUS_INTERFACE;
architecture BEHAVIOR of WF68K30L_BUS_INTERFACE is
type BUS_CTRL_STATES is (IDLE, START_CYCLE, DATA_C1C4);
type ARB_STATES is(IDLE, GRANT, WAIT_RELEASE_3WIRE);
type BUS_WIDTH_TYPE is(LONG_32, WORD, BYTE);
type TIME_SLICES is (IDLE, S0, S1, S2, S3, S4, S5);
signal ADR_10 : std_logic_vector(1 downto 0);
signal ADR_OFFSET : std_logic_vector(5 downto 0);
signal ADR_OUT_I : std_logic_vector(31 downto 0);
signal AERR_I : bit;
signal ARB_STATE : ARB_STATES := IDLE;
signal AVEC_In : std_logic;
signal BGACK_In : std_logic;
signal BR_In : std_logic;
signal BUS_CTRL_STATE : BUS_CTRL_STATES;
signal BUS_CYC_RDY : bit;
signal BUS_FLT : std_logic;
signal BUS_WIDTH : BUS_WIDTH_TYPE;
signal DATA_INMUX : std_logic_vector(31 downto 0);
signal DATA_RDY_I : bit;
signal DBUFFER : std_logic_vector(31 downto 0);
signal DSACK_In : std_logic_vector(1 downto 0);
signal DSACK_MEM : std_logic_vector(1 downto 0);
signal OCS_INH : bit;
signal HALT_In : std_logic;
signal HALTED : bit;
signal NEXT_ARB_STATE : ARB_STATES;
signal NEXT_BUS_CTRL_STATE : BUS_CTRL_STATES;
signal OBUFFER : std_logic_vector(15 downto 0);
signal OPCODE_ACCESS : bit;
signal OPCODE_RDY_I : bit;
signal READ_ACCESS : bit;
signal RESET_CPU_I : bit;
signal RESET_OUT_I : std_logic;
signal RETRY : bit;
signal SIZE_D : std_logic_vector(1 downto 0);
signal SIZE_I : std_logic_vector(1 downto 0);
signal SIZE_N : std_logic_vector(2 downto 0) := "000";
signal SLICE_CNT_N : std_logic_vector(2 downto 0);
signal SLICE_CNT_P : std_logic_vector(2 downto 0);
signal STERM_Dn : std_logic;
signal T_SLICE : TIME_SLICES;
signal WAITSTATES : bit;
signal WP_BUFFER : std_logic_vector(31 downto 0);
signal WRITE_ACCESS : bit;
begin
P_SYNC: process(CLK)
-- These flip flops synchronize external signals on the negative clock edge. This
-- meets the requirement of sampling these signals in the end of S2 for asynchronous
-- bus access. Be aware, that we have to buffer the RETRY signal to prevent the bus
-- controller of spurious or timing critical BERRn and/or HALTn signals. The logic
-- for BUS_FLT and RETRY is coded in a way that we have a bus error or a retry
-- condition but not both at the same time.
-- Note: there is no need to synchronize the already synchronous bus control signals
variable BERR_VARn : std_logic;
variable HALT_VARn : std_logic;
begin
if CLK = '0' and CLK' event then
DSACK_In <= DSACKn;
BR_In <= BRn;
BGACK_In <= BGACKn;
AVEC_In <= AVECn;
HALT_VARn := HALTn;
BERR_VARn := BERRn;
end if;
--
if CLK = '1' and CLK' event then
if BUS_CTRL_STATE = START_CYCLE then
AERR <= AERR_I; -- AERR_I is valid in this state.
else
AERR <= '0';
end if;
--
HALT_In <= HALTn or HALT_VARn;
--
if BUS_CTRL_STATE = DATA_C1C4 then
if (BERRn nand BERR_VARn) = '1' and (HALTn or HALT_VARn) = '0' and SIZE_N /= "000" then
RETRY <= '1';
elsif T_SLICE = IDLE and (BERRn = '1' and HALTn = '1' and BERR_VARn = '1' and HALT_VARn = '1') then
RETRY <= '0';
elsif RETRY = '0' then
BUS_FLT <= (BERRn nor BERR_VARn) and HALT_VARn and HALTn;
end if;
else
BUS_FLT <= '0';
RETRY <= '0';
end if;
--
STERM_Dn <= STERMn; -- Delay to update the SIZE_N and SIZE_M before BUS_CYC_RDY is asserted.
end if;
end process P_SYNC;
ACCESSTYPE: process
-- This logic stores the execution unit control
-- signals during the current bus access. This is
-- important for the bus control signals to be
-- stable during the complete bus access.
begin
wait until CLK = '1' and CLK' event;
if BUS_CTRL_STATE = START_CYCLE then
if READ_ACCESS = '1' or WRITE_ACCESS = '1' or OPCODE_ACCESS = '1' then
null; -- Do not start either new cycle.
elsif RD_REQ = '1' then
READ_ACCESS <= '1';
elsif WR_REQ = '1' then
WRITE_ACCESS <= '1';
elsif OPCODE_REQ = '1' then
OPCODE_ACCESS <= '1';
end if;
elsif AERR = '1' then -- Reject due to address error.
READ_ACCESS <= '0';
WRITE_ACCESS <= '0';
OPCODE_ACCESS <= '0';
elsif BUS_CTRL_STATE = DATA_C1C4 and NEXT_BUS_CTRL_STATE = IDLE and SIZE_N = "000" then
READ_ACCESS <= '0';
WRITE_ACCESS <= '0';
OPCODE_ACCESS <= '0';
end if;
end process ACCESSTYPE;
P_DF: process
-- This is the logic which provides the fault flags for data cycles and
-- input and output buffer information.
variable SIZEVAR : std_logic_vector(1 downto 0) := "00";
begin
wait until CLK = '1' and CLK' event;
if BUSY_EXH = '0' then -- Do not alter during exception processing.
case OP_SIZE is
when LONG => SIZEVAR := "10";
when WORD => SIZEVAR := "01";
when BYTE => SIZEVAR := "00";
end case;
--
if BUS_CTRL_STATE = START_CYCLE and NEXT_BUS_CTRL_STATE = DATA_C1C4 then
SSW_80 <= To_StdLogicVector('0' & RMC & not WR_REQ) & SIZEVAR & '0' & FC_IN;
elsif BUS_CTRL_STATE = DATA_C1C4 and (READ_ACCESS = '1' or WRITE_ACCESS = '1') and BUS_FLT = '1' then
SSW_80(8) <= '1';
end if;
OUTBUFFER <= WP_BUFFER; -- Used for exception stack frame type A and B.
INBUFFER <= DATA_INMUX; -- Used for exception stack frame type B.
end if;
end process P_DF;
WRITEBACK_INFO: process
-- This registers stor writeback relevant information.
begin
wait until CLK = '1' and CLK' event;
if BUS_CTRL_STATE = IDLE and NEXT_BUS_CTRL_STATE = START_CYCLE then -- Freeze during a bus cycle.
WP_BUFFER <= DATA_FROM_CORE;
end if;
end process WRITEBACK_INFO;
P_BUSWIDTH: process
-- These flip flops store the bus width information
-- during the current bus access.
begin
wait until CLK = '1' and CLK' event;
if BUS_CTRL_STATE = IDLE then
DSACK_MEM <= "11";
elsif DSACK_In /= "11" then
DSACK_MEM <= DSACK_In;
end if;
end process P_BUSWIDTH;
BUS_WIDTH <= WORD when DSACKn = "01" or DSACK_MEM = "01" else
BYTE when DSACKn = "10" or DSACK_MEM = "10" else
LONG_32; -- Also used during synchronous cycles.
BUS_BSY <= '1' when BUS_CTRL_STATE /= IDLE else '0';
PARTITIONING: process
-- This logic gives information about the remaining bus cycles The initial
-- size is sampled right before the bus acces. This requires the RD_REQ
-- and WR_REQ signals to work on the positive clock edge.
variable RESTORE_VAR : std_logic_vector(2 downto 0) := "000";
begin
wait until CLK = '1' and CLK' event;
if BUS_CTRL_STATE = DATA_C1C4 and T_SLICE = S1 then -- On positive clock edge.
RESTORE_VAR := SIZE_N; -- We need this initial value for early RETRY.
end if;
--
if RESET_CPU_I = '1' then
SIZE_N <= "000";
elsif BUS_CTRL_STATE /= DATA_C1C4 and NEXT_BUS_CTRL_STATE = DATA_C1C4 then
if RD_REQ = '1' or WR_REQ = '1' then
case OP_SIZE is
when LONG => SIZE_N <= "100";
when WORD => SIZE_N <= "010";
when BYTE => SIZE_N <= "001";
end case;
else -- OPCODE_ACCESS.
SIZE_N <= "010"; -- WORD.
end if;
end if;
-- Decrementing the size information:
-- In this logic all permutations are considered. This allows a dynamically changing bus size.
if RETRY = '1' then
SIZE_N <= RESTORE_VAR;
elsif BUS_CTRL_STATE = DATA_C1C4 and ((T_SLICE = S1 and STERMn = '0') or (T_SLICE = S3 and WAITSTATES = '0')) then -- On positive clock edge.
if BUS_WIDTH = LONG_32 and SIZE_N > x"3" and ADR_OUT_I(1 downto 0) = "01" then
SIZE_N <= SIZE_N - "11";
elsif BUS_WIDTH = LONG_32 and SIZE_N > x"2" and ADR_OUT_I(1 downto 0) = "10" then
SIZE_N <= SIZE_N - "10";
elsif BUS_WIDTH = LONG_32 and SIZE_N > x"1" and ADR_OUT_I(1 downto 0) = "11" then
SIZE_N <= SIZE_N - '1';
elsif BUS_WIDTH = LONG_32 then
SIZE_N <= "000";
--
elsif BUS_WIDTH = WORD and ADR_OUT_I(1 downto 0) = "11" then
SIZE_N <= SIZE_N - '1';
elsif BUS_WIDTH = WORD and ADR_OUT_I(1 downto 0) = "01" then
SIZE_N <= SIZE_N - '1';
elsif BUS_WIDTH = WORD and SIZE_N = "001" then
SIZE_N <= SIZE_N - '1';
elsif BUS_WIDTH = WORD then
SIZE_N <= SIZE_N - "10";
--
elsif BUS_WIDTH = BYTE then
SIZE_N <= SIZE_N - '1';
end if;
end if;
--
if (BUS_FLT = '1' and HALT_In = '1') then -- Abort bus cycle.
SIZE_N <= "000";
end if;
end process PARTITIONING;
SIZE_I <= SIZE_N(1 downto 0) when T_SLICE = S0 or T_SLICE = S1 else SIZE_D;
SIZE <= SIZE_I;
P_DELAY: process
-- This delay is responsible for a correct SIZE_I information. Use this, if the
-- process PARTITIONING works on the positive clock edge. The SIZE_I information
-- is delayed by half a clock cycle to be valid just in time of sampling the INMUX.
begin
wait until CLK = '1' and CLK' event;
SIZE_D <= SIZE_N(1 downto 0);
end process P_DELAY;
BUS_STATE_REG: process
-- This is the bus controller's state register.
begin
wait until CLK = '1' and CLK' event;
BUS_CTRL_STATE <= NEXT_BUS_CTRL_STATE;
end process BUS_STATE_REG;
BUS_CTRL_DEC: process(ADR_IN_P, ADR_OUT_I, ARB_STATE, BGACK_In, BR_In, BUS_CTRL_STATE, BUS_CYC_RDY, BUS_FLT, HALT_In,
OPCODE_ACCESS, OPCODE_REQ, RD_REQ, READ_ACCESS, RESET_CPU_I, RMC, SIZE_N, WR_REQ, WRITE_ACCESS)
-- This is the bus controller's state machine decoder. A SIZE_N count of "000" means that all bytes
-- to be transfered. After a bus transfer a value of x"0" indicates that no further bytes are required
-- for a bus transfer.
begin
case BUS_CTRL_STATE is
when IDLE =>
if RESET_CPU_I = '1' then
NEXT_BUS_CTRL_STATE <= IDLE; -- Reset condition (bus cycle terminated).
elsif HALT_In = '0' then
NEXT_BUS_CTRL_STATE <= IDLE; -- This is the 'HALT' condition.
elsif (BR_In = '0' and RMC = '0') or ARB_STATE /= IDLE or BGACK_In = '0' then
NEXT_BUS_CTRL_STATE <= IDLE; -- Arbitration, wait!
elsif RD_REQ = '1' and SIZE_N = "000" then
NEXT_BUS_CTRL_STATE <= START_CYCLE; -- New read cycle.
elsif WR_REQ = '1' and SIZE_N = "000" then
NEXT_BUS_CTRL_STATE <= START_CYCLE; -- New write cycle.
elsif OPCODE_REQ = '1' and SIZE_N = "000" then
NEXT_BUS_CTRL_STATE <= START_CYCLE; -- New read cycle.
elsif READ_ACCESS = '1' or WRITE_ACCESS = '1' or OPCODE_ACCESS = '1' then
NEXT_BUS_CTRL_STATE <= START_CYCLE; -- Pending (split) bus cycles.
else
NEXT_BUS_CTRL_STATE <= IDLE;
end if;
when START_CYCLE =>
if RD_REQ = '1' then
NEXT_BUS_CTRL_STATE <= DATA_C1C4;
elsif WR_REQ = '1' then
NEXT_BUS_CTRL_STATE <= DATA_C1C4;
elsif OPCODE_REQ = '1' and ADR_IN_P(0) = '1' then
NEXT_BUS_CTRL_STATE <= IDLE; -- Abort due to address error.
elsif OPCODE_REQ = '1' and ADR_IN_P(0) = '1' then
NEXT_BUS_CTRL_STATE <= IDLE; -- Abort due to address error.
elsif OPCODE_REQ = '1' then
NEXT_BUS_CTRL_STATE <= DATA_C1C4;
else
NEXT_BUS_CTRL_STATE <= IDLE;
end if;
when DATA_C1C4 =>
if BUS_CYC_RDY = '1' and SIZE_N = "000" then
NEXT_BUS_CTRL_STATE <= IDLE;
else
NEXT_BUS_CTRL_STATE <= DATA_C1C4;
end if;
end case;
end process BUS_CTRL_DEC;
P_ADR_OFFS: process
-- This process provides a temporary address offset during
-- bus access.
variable OFFSET_VAR : std_logic_vector(2 downto 0) := "000";
begin
wait until CLK = '1' and CLK' event;
if RESET_CPU_I = '1' then
OFFSET_VAR := "000";
elsif (T_SLICE = S2 and STERMn = '0') or T_SLICE = S3 then
case BUS_WIDTH is
when LONG_32 =>
case ADR_OUT_I(1 downto 0) is
when "11" => OFFSET_VAR := "001";
when "10" => OFFSET_VAR := "010";
when "01" => OFFSET_VAR := "011";
when others => OFFSET_VAR := "100";
end case;
when WORD =>
case ADR_OUT_I(1 downto 0) is
when "01" | "11" => OFFSET_VAR := "001";
when others => OFFSET_VAR := "010";
end case;
when BYTE =>
OFFSET_VAR := "001";
end case;
end if;
--
if RESET_CPU_I = '1' then
ADR_OFFSET <= (others => '0');
elsif RETRY = '1' then
null; -- Do not update if there is a retry cycle.
elsif BUS_CTRL_STATE /= IDLE and NEXT_BUS_CTRL_STATE = IDLE then
ADR_OFFSET <= (others => '0');
elsif BUS_CYC_RDY = '1' then
ADR_OFFSET <= ADR_OFFSET + OFFSET_VAR;
end if;
end process P_ADR_OFFS;
ADR_OUT_I <= ADR_IN_P + ADR_OFFSET;
ADR_OUT_P <= ADR_OUT_I;
P_ADR_10: process
-- This logic is registered to enhance the system performance concerning fmax.
begin
wait until CLK = '1' and CLK' event;
ADR_10 <= ADR_OUT_I(1 downto 0);
end process P_ADR_10;
-- Address and bus errors:
AERR_I <= '1' when BUS_CTRL_STATE = START_CYCLE and OPCODE_REQ = '1' and RD_REQ = '0' and WR_REQ = '0' and ADR_IN_P(0) = '1' else '0';
FC_OUT <= FC_IN;
-- The output multiplexer is as follows:
-- SIZE ADR Bytes (L = long word port, W = word port, B = Byte port, x = not used by either port)
-- 00 00 3 (L, W, B) 2 (L, W ) 1 (L ) 0 (L )
-- 00 01 3 ( B) 3 (L, W ) 2 (L ) 1 (L )
-- 00 10 3 ( W, B) 2 ( W ) 3 (L ) 2 (L )
-- 00 11 3 ( B) 2 ( W ) -----x----- 0 (L )
-- 11 00 2 (L, W, B) 1 (L, W ) 0 (L ) -----x-----
-- 11 01 2 ( B) 2 (L, W ) 1 (L ) 0 (L )
-- 11 10 1 ( W, B) 0 ( W ) 1 (L ) 0 (L )
-- 11 11 1 ( B) 1 ( W ) -----x----- 1 (L )
-- 10 00 1 (L, W, B) 0 (L, W ) -----x----- -----x-----
-- 10 01 1 ( B) 1 (L, W ) 0 (L ) -----x-----
-- 10 10 1 ( W, B) 0 ( W ) 1 (L ) 0 (L )
-- 10 11 1 ( B) 1 ( W ) -----x----- 1 (L )
-- 01 00 0 (L, W, B) -----x----- -----x----- -----x-----
-- 01 01 0 ( B) 0 (L, W ) -----x----- -----x-----
-- 01 10 0 ( W, B) -----x----- 0 (L ) -----x-----
-- 01 11 0 ( B) 0 ( W ) -----x----- 0 (L )
DATA_PORT_OUT <= -- Data output multiplexer.
-- LONG:
WP_BUFFER(31 downto 0) when SIZE_I = "00" and ADR_OUT_I(1 downto 0) = "00" else
WP_BUFFER(31 downto 24) & WP_BUFFER(31 downto 8) when SIZE_I = "00" and ADR_OUT_I(1 downto 0) = "01" else
WP_BUFFER(31 downto 16) & WP_BUFFER(31 downto 16) when SIZE_I = "00" and ADR_OUT_I(1 downto 0) = "10" else
WP_BUFFER(31 downto 24) & WP_BUFFER(31 downto 16) & WP_BUFFER(31 downto 24) when SIZE_I = "00" and ADR_OUT_I(1 downto 0) = "11" else
-- 3 bytes:
WP_BUFFER(23 downto 0) & WP_BUFFER(31 downto 24) when SIZE_I = x"3" and ADR_OUT_I(1 downto 0) = "00" else
WP_BUFFER(23 downto 16) & WP_BUFFER(23 downto 0) when SIZE_I = "11" and ADR_OUT_I(1 downto 0) = "01" else
WP_BUFFER(23 downto 8) & WP_BUFFER(23 downto 8) when SIZE_I = "11" and ADR_OUT_I(1 downto 0) = "10" else
WP_BUFFER(23 downto 16) & WP_BUFFER(23 downto 8) & WP_BUFFER(23 downto 16) when SIZE_I = "11" and ADR_OUT_I(1 downto 0) = "11" else
-- Word:
WP_BUFFER(15 downto 0) & WP_BUFFER(15 downto 0) when SIZE_I = "10" and ADR_OUT_I(1 downto 0) = "00" else
WP_BUFFER(15 downto 8) & WP_BUFFER(15 downto 0) & WP_BUFFER(15 downto 8) when SIZE_I = "10" and ADR_OUT_I(1 downto 0) = "01" else
WP_BUFFER(15 downto 0) & WP_BUFFER(15 downto 0) when SIZE_I = "10" and ADR_OUT_I(1 downto 0) = "10" else
WP_BUFFER(15 downto 8) & WP_BUFFER(15 downto 0) & WP_BUFFER(15 downto 8) when SIZE_I = "10" and ADR_OUT_I(1 downto 0) = "11" else
-- Byte:
WP_BUFFER(7 downto 0) & WP_BUFFER(7 downto 0) & WP_BUFFER(7 downto 0) & WP_BUFFER(7 downto 0); -- SIZE = "01".
IN_MUX: process
-- This is the input multiplexer which can handle up to four bytes.
begin
wait until CLK = '0' and CLK' event;
--
if ((T_SLICE = S2 or T_SLICE = S3) and STERMn = '0') or T_SLICE = S4 then
case BUS_WIDTH is
when BYTE =>
case SIZE_I is
when "00" => DATA_INMUX(31 downto 24) <= DATA_PORT_IN(31 downto 24); -- LONG.
when "11" => DATA_INMUX(23 downto 16) <= DATA_PORT_IN(31 downto 24); -- Three bytes.
when "10" => DATA_INMUX(15 downto 8) <= DATA_PORT_IN(31 downto 24); -- Word.
when others => DATA_INMUX(7 downto 0) <= DATA_PORT_IN(31 downto 24); -- Byte.
end case;
when WORD =>
case SIZE_I is
when "01" => -- Byte.
case ADR_10 is
when "00" | "10" => DATA_INMUX(7 downto 0) <= DATA_PORT_IN(31 downto 24);
when others => DATA_INMUX(7 downto 0) <= DATA_PORT_IN(23 downto 16); -- "01", "11".
end case;
when "10" => -- Word.
case ADR_10 is
when "00" => DATA_INMUX(15 downto 0) <= DATA_PORT_IN(31 downto 16);
when "01" => DATA_INMUX(15 downto 8) <= DATA_PORT_IN(23 downto 16);
when "10" => DATA_INMUX(15 downto 0) <= DATA_PORT_IN(31 downto 16);
when others => DATA_INMUX(15 downto 8) <= DATA_PORT_IN(23 downto 16); -- "11".
end case;
when "11" => -- Three bytes.
case ADR_10 is
when "00" => DATA_INMUX(23 downto 8) <= DATA_PORT_IN(31 downto 16);
when "01" => DATA_INMUX(23 downto 16) <= DATA_PORT_IN(23 downto 16);
when "10" => DATA_INMUX(23 downto 8) <= DATA_PORT_IN(31 downto 16);
when others => DATA_INMUX(23 downto 16) <= DATA_PORT_IN(23 downto 16); -- "11".
end case;
when others => -- "00" = LONG.
case ADR_10 is
when "00" => DATA_INMUX(31 downto 16) <= DATA_PORT_IN(31 downto 16);
when "01" => DATA_INMUX(31 downto 24) <= DATA_PORT_IN(23 downto 16);
when "10" => DATA_INMUX(31 downto 16) <= DATA_PORT_IN(31 downto 16);
when others => DATA_INMUX(31 downto 24) <= DATA_PORT_IN(23 downto 16); -- "11".
end case;
end case;
when LONG_32 =>
case SIZE_I is
when "01" => -- Byte.
case ADR_10 is
when "00" => DATA_INMUX(7 downto 0) <= DATA_PORT_IN(31 downto 24);
when "01" => DATA_INMUX(7 downto 0) <= DATA_PORT_IN(23 downto 16);
when "10" => DATA_INMUX(7 downto 0) <= DATA_PORT_IN(15 downto 8);
when others => DATA_INMUX(7 downto 0) <= DATA_PORT_IN(7 downto 0); -- "11".
end case;
when "10" => -- Word.
case ADR_10 is
when "00" => DATA_INMUX(15 downto 0) <= DATA_PORT_IN(31 downto 16);
when "01" => DATA_INMUX(15 downto 0) <= DATA_PORT_IN(23 downto 8);
when "10" => DATA_INMUX(15 downto 0) <= DATA_PORT_IN(15 downto 0);
when others => DATA_INMUX(15 downto 8) <= DATA_PORT_IN(7 downto 0); -- "11".
end case;
when "11" => -- Three bytes.
case ADR_10 is
when "00" => DATA_INMUX(23 downto 0) <= DATA_PORT_IN(31 downto 8);
when "01" => DATA_INMUX(23 downto 0) <= DATA_PORT_IN(23 downto 0);
when "10" => DATA_INMUX(23 downto 8) <= DATA_PORT_IN(15 downto 0);
when others => DATA_INMUX(23 downto 16) <= DATA_PORT_IN(7 downto 0); -- "11".
end case;
when others => -- "00" = LONG.
case ADR_10 is
when "00" => DATA_INMUX(31 downto 0) <= DATA_PORT_IN(31 downto 0);
when "01" => DATA_INMUX(31 downto 8) <= DATA_PORT_IN(23 downto 0);
when "10" => DATA_INMUX(31 downto 16) <= DATA_PORT_IN(15 downto 0);
when others => DATA_INMUX(31 downto 24) <= DATA_PORT_IN(7 downto 0); -- "11".
end case;
end case;
end case;
end if;
end process IN_MUX;
VALIDATION: process
-- These flip flops detect a fault during the read operation over one or
-- several bytes or during the write operation.
begin
wait until CLK = '1' and CLK' event;
--
if RESET_CPU_I = '1' then
OPCODE_VALID <= '1';
elsif OPCODE_ACCESS = '1' and BUS_CTRL_STATE = DATA_C1C4 and BUS_FLT = '1' then
OPCODE_VALID <= '0';
elsif OPCODE_RDY_I = '1' then
OPCODE_VALID <= '1'; -- Reset after use, TRAP_BERR is asserted during DATA_RDY.
end if;
--
if RESET_CPU_I = '1' then
DATA_VALID <= '1';
elsif BUS_CTRL_STATE = DATA_C1C4 and BUS_FLT = '1' then
DATA_VALID <= '0';
elsif DATA_RDY_I = '1' then
DATA_VALID <= '1'; -- Reset after use, TRAP_BERR is asserted during DATA_RDY.
end if;
end process VALIDATION;
PREFETCH_BUFFERS: process
-- These are the data and the operation code input registers. After a last read to the registered
-- input multiplexer, the respective data is copied from the input multiplexer to these buffers.
-- The opcode buffer is always written with 32 bit data. The data buffers may contain invalid bytes
-- in case of word or byte data size.
variable DBUFFER_MEM : std_logic_vector(31 downto 8) := x"000000";
variable RDY_VAR : bit := '0';
begin
wait until CLK = '1' and CLK' event;
--
OPCODE_RDY_I <= '0'; -- This is a strobe.
DATA_RDY_I <= '0'; -- This is a strobe.
--
-- The following variable is responsible, that the _RDY signals are
-- always strobes.
if DATA_RDY_I = '1' or OPCODE_RDY_I = '1' then
RDY_VAR := '0';
elsif BUS_CTRL_STATE = START_CYCLE then
RDY_VAR := '1';
end if;
-- Opcode cycle:
if AERR_I = '1' then
OPCODE_RDY_I <= '1';
elsif OPCODE_ACCESS = '1' and BUS_CTRL_STATE = DATA_C1C4 and BUS_CYC_RDY = '1' and SIZE_N = "000" then
-- Instruction prefetches are always long and on word boundaries.
-- The word is available after the first word read.
OBUFFER <= DATA_INMUX(15 downto 0);
OPCODE_RDY_I <= RDY_VAR;
end if;
-- Data cycle:
if WRITE_ACCESS = '1' and BUS_CTRL_STATE = DATA_C1C4 and BUS_CYC_RDY = '1' and SIZE_N = "000" then
DATA_RDY_I <= RDY_VAR;
elsif READ_ACCESS = '1' and BUS_CTRL_STATE = DATA_C1C4 and BUS_CYC_RDY = '1' then
case OP_SIZE is
when LONG =>
if SIZE_N = "000" then
DBUFFER <= DATA_INMUX;
DATA_RDY_I <= RDY_VAR;
end if;
when WORD =>
if SIZE_N = "000" then
DBUFFER <= x"0000" & DATA_INMUX(15 downto 0);
DATA_RDY_I <= RDY_VAR;
end if;
when BYTE => -- Byte always aligned.
DATA_RDY_I <= RDY_VAR;
DBUFFER <= x"000000" & DATA_INMUX(7 downto 0);
end case;
end if;
end process PREFETCH_BUFFERS;
DATA_RDY <= DATA_RDY_I;
OPCODE_RDY <= OPCODE_RDY_I;
DATA_TO_CORE <= DBUFFER;
OPCODE_TO_CORE <= OBUFFER;
WAITSTATES <= '0' when T_SLICE /= S3 else
'1' when RESET_OUT_I = '1' else -- No bus fault during RESET instruction.
'0' when DSACK_In /= "11" else -- For asynchronous bus cycles.
'0' when STERMn = '0' else -- For synchronous bus cycles.
'0' when ADR_IN_P(19 downto 16) = x"F" and AVEC_In = '0' else -- Interrupt acknowledge space cycle.
'0' when BUS_FLT = '1' else -- In case of a bus error;
'0' when RESET_CPU_I = '1' else '1'; -- A CPU reset terminates the current bus cycle.
SLICES: process(CLK)
-- This process provides the central timing for the read, write and read modify write cycle as also
-- for the bus arbitration procedure. Be aware, that the bus controller state machine changes it's
-- state on the positive clock edge. The BUS_CYC_RDY signal is asserted during S3 or S5. So the
-- slice counter working on the positive clock edge may change it's state.
begin
if CLK = '1' and CLK' event then
if BUS_CTRL_STATE = IDLE then
SLICE_CNT_P <= "111"; -- Init.
elsif RETRY = '1' then
SLICE_CNT_P <= "111"; -- Stay in IDLE, go to IDLE.
elsif BUS_CTRL_STATE /= IDLE and NEXT_BUS_CTRL_STATE = IDLE then
SLICE_CNT_P <= "111"; -- Init.
elsif SLICE_CNT_P = "001" and STERMn = '0' then -- Synchronous cycle.
SLICE_CNT_P <= "110"; -- Ready.
elsif SLICE_CNT_P = "010" then
if RETRY = '1' then
SLICE_CNT_P <= "111"; -- Go IDLE.
elsif BUS_CTRL_STATE = DATA_C1C4 and NEXT_BUS_CTRL_STATE = IDLE then
SLICE_CNT_P <= "111"; -- Ready.
else
SLICE_CNT_P <= "000"; -- Go on.
end if;
elsif WAITSTATES = '0' then
SLICE_CNT_P <= SLICE_CNT_P + '1'; -- Cycle active.
end if;
end if;
--
if CLK = '0' and CLK' event then
SLICE_CNT_N <= SLICE_CNT_P; -- Follow the P counter.
end if;
end process SLICES;
T_SLICE <= S0 when SLICE_CNT_P = "000" and SLICE_CNT_N = "111" else
S1 when SLICE_CNT_P = "000" and SLICE_CNT_N = "000" else
S2 when SLICE_CNT_P = "001" and SLICE_CNT_N = "000" else
S3 when SLICE_CNT_P = "001" and SLICE_CNT_N = "001" else
S4 when SLICE_CNT_P = "010" and SLICE_CNT_N = "001" else
S5 when SLICE_CNT_P = "010" and SLICE_CNT_N = "010" else
S3 when SLICE_CNT_P = "110" else -- This is a waitstate cycle for synchronous bus cycles to update SIZE_N before latching data.
S0 when SLICE_CNT_P = "000" and SLICE_CNT_N = "010" else IDLE; -- Rollover from state S5 to S0.
P_OCS: process
-- This flip flop enables the output of the OCSn signal for
-- the first bus cycle and locks OCSn for all other bus cycles.
-- ECSn is locked only during cache burst access.
begin
wait until CLK = '1' and CLK' event;
--
if BUS_CTRL_STATE = START_CYCLE and NEXT_BUS_CTRL_STATE /= IDLE then
OCS_INH <= '0';
elsif BUS_CYC_RDY = '1' and RETRY = '0' then -- No inhibit if first portion results in a retry cycle.
OCS_INH <= '1';
end if;
end process P_OCS;
-- Bus control signals:
RWn <= '0' when WRITE_ACCESS = '1' and BUS_CTRL_STATE = DATA_C1C4 else '1';
RMCn <= '0' when RMC = '1' else '1';
ECSn <= '0' when T_SLICE = S0 else '1';
OCSn <= '0' when T_SLICE = S0 and OCS_INH = '0' else '1';
ASn <= '0' when T_SLICE = S1 or T_SLICE = S2 or T_SLICE = S3 or T_SLICE = S4 else '1';
DSn <= '0' when (T_SLICE = S3 or T_SLICE = S4 or T_SLICE = S5) and WRITE_ACCESS = '1' else -- Write.
'0' when T_SLICE = S1 or T_SLICE = S2 or T_SLICE = S3 or T_SLICE = S4 else '1'; -- Read.
DBENn <= '0' when (T_SLICE = S1 or T_SLICE = S2 or T_SLICE = S3 or T_SLICE = S4 or T_SLICE = S5) and WRITE_ACCESS = '1' else -- Write.
'0' when T_SLICE = S2 or T_SLICE = S3 or T_SLICE = S4 else '1'; -- Read.
-- Bus tri state controls:
BUS_EN <= '1' when ARB_STATE = IDLE and RESET_CPU_I = '0' else '0';
DATA_PORT_EN <= '1' when WRITE_ACCESS = '1' and ARB_STATE = IDLE and RESET_CPU_I = '0' else '0';
-- Progress controls:
BUS_CYC_RDY <= '0' when RETRY = '1' else
'1' when STERM_Dn = '0' else -- Synchronous cycles. STERMn delayed to update the SIZE_N and SIZE_M before BUS_CYC_RDY is asserted.
'1' when T_SLICE = S5 else '0'; -- Asynchronous cycles.
-- Bus arbitration:
ARB_REG: process
-- This is the arbiters state register.
begin
wait until CLK = '1' and CLK' event;
--
if RESET_CPU_I = '1' then
ARB_STATE <= IDLE;
else
ARB_STATE <= NEXT_ARB_STATE;
end if;
end process ARB_REG;
ARB_DEC: process(ARB_STATE, BGACK_In, BR_In, BUS_CTRL_STATE, RETRY, RMC)
-- This is the bus arbitration state machine's decoder. It can handle single-, two-
-- or three wire arbitration. The two wire arbitration is done in the GRANT state
-- by negating BRn.
begin
case ARB_STATE is
when IDLE =>
if RMC = '1' and RETRY = '0' then
NEXT_ARB_STATE <= IDLE; -- Arbitration in RETRY operation is possible.
elsif BGACK_In = '0' and BUS_CTRL_STATE = IDLE then -- This is the single wire arbitration.
NEXT_ARB_STATE <= WAIT_RELEASE_3WIRE;
elsif BR_In = '0' and BUS_CTRL_STATE = IDLE then -- Wait until the bus is free.
NEXT_ARB_STATE <= GRANT;
else
NEXT_ARB_STATE <= IDLE;
end if;
when GRANT =>
if BGACK_In = '0' then
NEXT_ARB_STATE <= WAIT_RELEASE_3WIRE;
elsif BR_In = '1' then
NEXT_ARB_STATE <= IDLE; -- Resume normal operation.
else
NEXT_ARB_STATE <= GRANT;
end if;
when WAIT_RELEASE_3WIRE =>
if BGACK_In = '1' and BR_In = '0' then
NEXT_ARB_STATE <= GRANT; -- Re-enter new arbitration.
elsif BGACK_In = '1' then
NEXT_ARB_STATE <= IDLE;
else
NEXT_ARB_STATE <= WAIT_RELEASE_3WIRE;
end if;
end case;
end process ARB_DEC;
BGn <= '0' when ARB_STATE = GRANT else '1';
-- RESET logic:
RESET_FILTER: process
-- This process filters the incoming reset pin.
-- If RESET_IN and HALT_In are asserted together for longer
-- than 10 clock cycles over the execution of a CPU reset
-- command, the CPU reset is released.
variable STARTUP : boolean := false;
variable TMP : std_logic_vector(3 downto 0) := x"0";
begin
wait until CLK = '1' and CLK' event;
--
if RESET_IN = '1' and HALT_In = '0' and RESET_OUT_I = '0' and TMP < x"F" then
TMP := TMP + '1';
elsif RESET_IN = '0' or HALT_In = '1' or RESET_OUT_I = '1' then
TMP := x"0";
end if;
if TMP > x"A" then
RESET_CPU_I <= '1'; -- Release internal reset.
STARTUP := true;
elsif STARTUP = false then
RESET_CPU_I <= '1';
else
RESET_CPU_I <= '0';
end if;
end process RESET_FILTER;
RESET_TIMER: process
-- This logic is responsible for the assertion of the
-- reset output for 512 clock cycles, during the reset
-- command. The LOCK variable avoids re-initialisation
-- of the counter in the case that the RESET_EN is no
-- strobe.
variable TMP : std_logic_vector(8 downto 0) := "000000000";
begin
wait until CLK = '1' and CLK' event;
--
if RESET_STRB = '1' or TMP > "000000000" then
RESET_OUT_I <= '1';
else
RESET_OUT_I <= '0';
end if;
--
if RESET_STRB = '1' then
TMP := "111111111"; -- 512 initial value.
elsif TMP > "000000000" then
TMP := TMP - '1';
end if;
end process RESET_TIMER;
RESET_CPU <= RESET_CPU_I;
RESET_OUT <= RESET_OUT_I;
end BEHAVIOR;