|
|
|
library ieee;
|
|
|
|
use ieee.std_logic_1164.all;
|
|
|
|
use ieee.numeric_std.all;
|
|
|
|
use ieee.math_real.all;
|
|
|
|
use std.textio.all;
|
|
|
|
use std.env.stop;
|
|
|
|
|
|
|
|
library work;
|
|
|
|
use work.common.all;
|
|
|
|
use work.wishbone_types.all;
|
|
|
|
|
|
|
|
|
|
|
|
-- Memory map. *** Keep include/microwatt_soc.h updated on changes ***
|
|
|
|
--
|
|
|
|
-- Main bus:
|
|
|
|
-- 0x00000000: Block RAM (MEMORY_SIZE) or DRAM depending on syscon
|
|
|
|
-- 0x40000000: DRAM (when present)
|
|
|
|
-- 0x80000000: Block RAM (aliased & repeated)
|
|
|
|
|
|
|
|
-- IO Bus:
|
|
|
|
-- 0xc0000000: SYSCON
|
|
|
|
-- 0xc0002000: UART0
|
|
|
|
-- 0xc0003000: UART1 (if any)
|
|
|
|
-- 0xc0004000: XICS ICP
|
|
|
|
-- 0xc0005000: XICS ICS
|
|
|
|
-- 0xc0006000: SPI Flash controller
|
|
|
|
-- 0xc8nnnnnn: External IO bus
|
|
|
|
-- 0xf0000000: Flash "ROM" mapping
|
|
|
|
-- 0xff000000: DRAM init code (if any) or flash ROM (**)
|
|
|
|
|
|
|
|
-- External IO bus:
|
|
|
|
-- 0xc8000000: LiteDRAM control (CSRs)
|
|
|
|
-- 0xc8020000: LiteEth CSRs (*)
|
|
|
|
-- 0xc8030000: LiteEth MMIO (*)
|
|
|
|
|
|
|
|
-- (*) LiteEth must be a single aligned 32KB block as the CSRs and MMIOs
|
|
|
|
-- are actually decoded as a single wishbone which LiteEth will
|
|
|
|
-- internally split based on bit 16.
|
|
|
|
|
|
|
|
-- (**) DRAM init code is currently special and goes to the external
|
|
|
|
-- IO bus, this will be fixed when it's moved out of litedram and
|
|
|
|
-- into the main SoC once we have a common "firmware".
|
|
|
|
|
|
|
|
-- Interrupt numbers:
|
|
|
|
--
|
|
|
|
-- 0 : UART0
|
|
|
|
-- 1 : Ethernet
|
|
|
|
|
|
|
|
entity soc is
|
|
|
|
generic (
|
|
|
|
MEMORY_SIZE : natural;
|
|
|
|
RAM_INIT_FILE : string;
|
|
|
|
CLK_FREQ : positive;
|
|
|
|
SIM : boolean;
|
|
|
|
DISABLE_FLATTEN_CORE : boolean := false;
|
|
|
|
HAS_DRAM : boolean := false;
|
|
|
|
DRAM_SIZE : integer := 0;
|
|
|
|
DRAM_INIT_SIZE : integer := 0;
|
|
|
|
HAS_SPI_FLASH : boolean := false;
|
|
|
|
SPI_FLASH_DLINES : positive := 1;
|
|
|
|
SPI_FLASH_OFFSET : integer := 0;
|
|
|
|
SPI_FLASH_DEF_CKDV : natural := 2;
|
|
|
|
SPI_FLASH_DEF_QUAD : boolean := false;
|
|
|
|
LOG_LENGTH : natural := 512;
|
|
|
|
HAS_LITEETH : boolean := false;
|
|
|
|
UART0_IS_16550 : boolean := true;
|
|
|
|
HAS_UART1 : boolean := false
|
|
|
|
);
|
|
|
|
port(
|
|
|
|
rst : in std_ulogic;
|
|
|
|
system_clk : in std_ulogic;
|
|
|
|
|
|
|
|
-- "Large" (64-bit) DRAM wishbone
|
|
|
|
wb_dram_in : out wishbone_master_out;
|
|
|
|
wb_dram_out : in wishbone_slave_out := wishbone_slave_out_init;
|
|
|
|
|
|
|
|
-- "Small" (32-bit) external IO wishbone
|
|
|
|
wb_ext_io_in : out wb_io_master_out;
|
|
|
|
wb_ext_io_out : in wb_io_slave_out := wb_io_slave_out_init;
|
|
|
|
wb_ext_is_dram_csr : out std_ulogic;
|
|
|
|
wb_ext_is_dram_init : out std_ulogic;
|
|
|
|
wb_ext_is_eth : out std_ulogic;
|
|
|
|
|
|
|
|
-- External interrupts
|
|
|
|
ext_irq_eth : in std_ulogic := '0';
|
|
|
|
|
|
|
|
-- UART0 signals:
|
|
|
|
uart0_txd : out std_ulogic;
|
|
|
|
uart0_rxd : in std_ulogic := '0';
|
|
|
|
|
|
|
|
-- UART1 signals:
|
|
|
|
uart1_txd : out std_ulogic;
|
|
|
|
uart1_rxd : in std_ulogic := '0';
|
|
|
|
|
|
|
|
-- SPI Flash signals
|
|
|
|
spi_flash_sck : out std_ulogic;
|
|
|
|
spi_flash_cs_n : out std_ulogic;
|
|
|
|
spi_flash_sdat_o : out std_ulogic_vector(SPI_FLASH_DLINES-1 downto 0);
|
|
|
|
spi_flash_sdat_oe : out std_ulogic_vector(SPI_FLASH_DLINES-1 downto 0);
|
|
|
|
spi_flash_sdat_i : in std_ulogic_vector(SPI_FLASH_DLINES-1 downto 0) := (others => '1');
|
|
|
|
|
|
|
|
-- DRAM controller signals
|
|
|
|
alt_reset : in std_ulogic := '0'
|
|
|
|
);
|
|
|
|
end entity soc;
|
|
|
|
|
|
|
|
architecture behaviour of soc is
|
|
|
|
|
|
|
|
-- Wishbone master signals:
|
|
|
|
signal wishbone_dcore_in : wishbone_slave_out;
|
|
|
|
signal wishbone_dcore_out : wishbone_master_out;
|
|
|
|
signal wishbone_icore_in : wishbone_slave_out;
|
|
|
|
signal wishbone_icore_out : wishbone_master_out;
|
|
|
|
signal wishbone_debug_in : wishbone_slave_out;
|
|
|
|
signal wishbone_debug_out : wishbone_master_out;
|
|
|
|
|
|
|
|
-- Arbiter array (ghdl doesnt' support assigning the array
|
|
|
|
-- elements in the entity instantiation)
|
|
|
|
constant NUM_WB_MASTERS : positive := 3;
|
|
|
|
signal wb_masters_out : wishbone_master_out_vector(0 to NUM_WB_MASTERS-1);
|
|
|
|
signal wb_masters_in : wishbone_slave_out_vector(0 to NUM_WB_MASTERS-1);
|
|
|
|
|
|
|
|
-- Wishbone master (output of arbiter):
|
|
|
|
signal wb_master_in : wishbone_slave_out;
|
|
|
|
signal wb_master_out : wishbone_master_out;
|
|
|
|
|
|
|
|
-- Main "IO" bus, from main slave decoder to the latch
|
|
|
|
signal wb_io_in : wishbone_master_out;
|
|
|
|
signal wb_io_out : wishbone_slave_out;
|
|
|
|
|
|
|
|
-- Secondary (smaller) IO bus after the IO bus latch
|
|
|
|
signal wb_sio_out : wb_io_master_out;
|
|
|
|
signal wb_sio_in : wb_io_slave_out;
|
|
|
|
|
|
|
|
-- Syscon signals
|
|
|
|
signal dram_at_0 : std_ulogic;
|
|
|
|
signal do_core_reset : std_ulogic;
|
|
|
|
signal wb_syscon_in : wb_io_master_out;
|
|
|
|
signal wb_syscon_out : wb_io_slave_out;
|
|
|
|
|
|
|
|
-- UART0 signals:
|
|
|
|
signal wb_uart0_in : wb_io_master_out;
|
|
|
|
signal wb_uart0_out : wb_io_slave_out;
|
|
|
|
signal uart0_dat8 : std_ulogic_vector(7 downto 0);
|
|
|
|
signal uart0_irq : std_ulogic;
|
|
|
|
|
|
|
|
-- UART1 signals:
|
|
|
|
signal wb_uart1_in : wb_io_master_out;
|
|
|
|
signal wb_uart1_out : wb_io_slave_out;
|
|
|
|
signal uart1_dat8 : std_ulogic_vector(7 downto 0);
|
|
|
|
signal uart1_irq : std_ulogic;
|
|
|
|
|
|
|
|
-- SPI Flash controller signals:
|
|
|
|
signal wb_spiflash_in : wb_io_master_out;
|
|
|
|
signal wb_spiflash_out : wb_io_slave_out;
|
|
|
|
signal wb_spiflash_is_reg : std_ulogic;
|
|
|
|
signal wb_spiflash_is_map : std_ulogic;
|
|
|
|
|
|
|
|
-- XICS signals:
|
|
|
|
signal wb_xics_icp_in : wb_io_master_out;
|
|
|
|
signal wb_xics_icp_out : wb_io_slave_out;
|
|
|
|
signal wb_xics_ics_in : wb_io_master_out;
|
|
|
|
signal wb_xics_ics_out : wb_io_slave_out;
|
|
|
|
signal int_level_in : std_ulogic_vector(15 downto 0);
|
|
|
|
signal ics_to_icp : ics_to_icp_t;
|
|
|
|
signal core_ext_irq : std_ulogic;
|
|
|
|
|
|
|
|
-- Main memory signals:
|
|
|
|
signal wb_bram_in : wishbone_master_out;
|
|
|
|
signal wb_bram_out : wishbone_slave_out;
|
|
|
|
|
|
|
|
-- DMI debug bus signals
|
|
|
|
signal dmi_addr : std_ulogic_vector(7 downto 0);
|
|
|
|
signal dmi_din : std_ulogic_vector(63 downto 0);
|
|
|
|
signal dmi_dout : std_ulogic_vector(63 downto 0);
|
|
|
|
signal dmi_req : std_ulogic;
|
|
|
|
signal dmi_wr : std_ulogic;
|
|
|
|
signal dmi_ack : std_ulogic;
|
|
|
|
|
|
|
|
-- Per slave DMI signals
|
|
|
|
signal dmi_wb_dout : std_ulogic_vector(63 downto 0);
|
|
|
|
signal dmi_wb_req : std_ulogic;
|
|
|
|
signal dmi_wb_ack : std_ulogic;
|
|
|
|
signal dmi_core_dout : std_ulogic_vector(63 downto 0);
|
|
|
|
signal dmi_core_req : std_ulogic;
|
|
|
|
signal dmi_core_ack : std_ulogic;
|
|
|
|
|
|
|
|
-- Delayed/latched resets and alt_reset
|
|
|
|
signal rst_core : std_ulogic := '1';
|
|
|
|
signal rst_uart : std_ulogic := '1';
|
|
|
|
signal rst_xics : std_ulogic := '1';
|
|
|
|
signal rst_spi : std_ulogic := '1';
|
|
|
|
signal rst_bram : std_ulogic := '1';
|
|
|
|
signal rst_dtm : std_ulogic := '1';
|
|
|
|
signal rst_wbar : std_ulogic := '1';
|
|
|
|
signal rst_wbdb : std_ulogic := '1';
|
|
|
|
signal alt_reset_d : std_ulogic;
|
|
|
|
|
|
|
|
-- IO branch split:
|
|
|
|
type slave_io_type is (SLAVE_IO_SYSCON,
|
|
|
|
SLAVE_IO_UART,
|
|
|
|
SLAVE_IO_ICP,
|
|
|
|
SLAVE_IO_ICS,
|
|
|
|
SLAVE_IO_UART1,
|
|
|
|
SLAVE_IO_SPI_FLASH_REG,
|
|
|
|
SLAVE_IO_SPI_FLASH_MAP,
|
|
|
|
SLAVE_IO_EXTERNAL,
|
|
|
|
SLAVE_IO_NONE);
|
|
|
|
signal slave_io_dbg : slave_io_type;
|
|
|
|
|
|
|
|
-- This is the component exported by the 16550 compatible
|
|
|
|
-- UART from FuseSoC.
|
|
|
|
--
|
|
|
|
component uart_top port (
|
|
|
|
wb_clk_i : in std_ulogic;
|
|
|
|
wb_rst_i : in std_ulogic;
|
|
|
|
wb_adr_i : in std_ulogic_vector(2 downto 0);
|
|
|
|
wb_dat_i : in std_ulogic_vector(7 downto 0);
|
|
|
|
wb_dat_o : out std_ulogic_vector(7 downto 0);
|
|
|
|
wb_we_i : in std_ulogic;
|
|
|
|
wb_stb_i : in std_ulogic;
|
|
|
|
wb_cyc_i : in std_ulogic;
|
|
|
|
wb_ack_o : out std_ulogic;
|
|
|
|
int_o : out std_ulogic;
|
|
|
|
stx_pad_o : out std_ulogic;
|
|
|
|
srx_pad_i : in std_ulogic;
|
|
|
|
rts_pad_o : out std_ulogic;
|
|
|
|
cts_pad_i : in std_ulogic;
|
|
|
|
dtr_pad_o : out std_ulogic;
|
|
|
|
dsr_pad_i : in std_ulogic;
|
|
|
|
ri_pad_i : in std_ulogic;
|
|
|
|
dcd_pad_i : in std_ulogic
|
|
|
|
);
|
|
|
|
end component;
|
|
|
|
begin
|
|
|
|
|
|
|
|
resets: process(system_clk)
|
|
|
|
begin
|
|
|
|
if rising_edge(system_clk) then
|
|
|
|
rst_core <= rst or do_core_reset;
|
|
|
|
rst_uart <= rst;
|
|
|
|
rst_spi <= rst;
|
|
|
|
rst_xics <= rst;
|
|
|
|
rst_bram <= rst;
|
|
|
|
rst_dtm <= rst;
|
|
|
|
rst_wbar <= rst;
|
|
|
|
rst_wbdb <= rst;
|
|
|
|
alt_reset_d <= alt_reset;
|
|
|
|
end if;
|
|
|
|
end process;
|
|
|
|
|
|
|
|
-- Processor core
|
|
|
|
processor: entity work.core
|
|
|
|
generic map(
|
|
|
|
SIM => SIM,
|
|
|
|
DISABLE_FLATTEN => DISABLE_FLATTEN_CORE,
|
|
|
|
ALT_RESET_ADDRESS => (23 downto 0 => '0', others => '1'),
|
|
|
|
LOG_LENGTH => LOG_LENGTH
|
|
|
|
)
|
|
|
|
port map(
|
|
|
|
clk => system_clk,
|
|
|
|
rst => rst_core,
|
|
|
|
alt_reset => alt_reset_d,
|
|
|
|
wishbone_insn_in => wishbone_icore_in,
|
|
|
|
wishbone_insn_out => wishbone_icore_out,
|
|
|
|
wishbone_data_in => wishbone_dcore_in,
|
|
|
|
wishbone_data_out => wishbone_dcore_out,
|
|
|
|
dmi_addr => dmi_addr(3 downto 0),
|
|
|
|
dmi_dout => dmi_core_dout,
|
|
|
|
dmi_din => dmi_dout,
|
|
|
|
dmi_wr => dmi_wr,
|
|
|
|
dmi_ack => dmi_core_ack,
|
|
|
|
dmi_req => dmi_core_req,
|
|
|
|
ext_irq => core_ext_irq
|
|
|
|
);
|
|
|
|
|
|
|
|
-- Wishbone bus master arbiter & mux
|
|
|
|
wb_masters_out <= (0 => wishbone_dcore_out,
|
|
|
|
1 => wishbone_icore_out,
|
|
|
|
2 => wishbone_debug_out);
|
|
|
|
wishbone_dcore_in <= wb_masters_in(0);
|
|
|
|
wishbone_icore_in <= wb_masters_in(1);
|
|
|
|
wishbone_debug_in <= wb_masters_in(2);
|
|
|
|
wishbone_arbiter_0: entity work.wishbone_arbiter
|
|
|
|
generic map(
|
|
|
|
NUM_MASTERS => NUM_WB_MASTERS
|
|
|
|
)
|
|
|
|
port map(
|
|
|
|
clk => system_clk,
|
|
|
|
rst => rst_wbar,
|
|
|
|
wb_masters_in => wb_masters_out,
|
|
|
|
wb_masters_out => wb_masters_in,
|
|
|
|
wb_slave_out => wb_master_out,
|
|
|
|
wb_slave_in => wb_master_in
|
|
|
|
);
|
|
|
|
|
|
|
|
-- Top level Wishbone slaves address decoder & mux
|
|
|
|
--
|
|
|
|
-- From CPU to BRAM, DRAM, IO, selected on top 3 bits and dram_at_0
|
|
|
|
-- 0000 - BRAM
|
|
|
|
-- 0001 - DRAM
|
|
|
|
-- 01xx - DRAM
|
|
|
|
-- 10xx - BRAM
|
|
|
|
-- 11xx - IO
|
|
|
|
--
|
|
|
|
slave_top_intercon: process(wb_master_out, wb_bram_out, wb_dram_out, wb_io_out, dram_at_0)
|
|
|
|
type slave_top_type is (SLAVE_TOP_BRAM,
|
|
|
|
SLAVE_TOP_DRAM,
|
|
|
|
SLAVE_TOP_IO);
|
|
|
|
variable slave_top : slave_top_type;
|
|
|
|
variable top_decode : std_ulogic_vector(3 downto 0);
|
|
|
|
begin
|
|
|
|
-- Top-level address decoder
|
|
|
|
top_decode := wb_master_out.adr(31 downto 29) & dram_at_0;
|
|
|
|
slave_top := SLAVE_TOP_BRAM;
|
|
|
|
if std_match(top_decode, "0000") then
|
|
|
|
slave_top := SLAVE_TOP_BRAM;
|
|
|
|
elsif std_match(top_decode, "0001") then
|
|
|
|
slave_top := SLAVE_TOP_DRAM;
|
|
|
|
elsif std_match(top_decode, "01--") then
|
|
|
|
slave_top := SLAVE_TOP_DRAM;
|
|
|
|
elsif std_match(top_decode, "10--") then
|
|
|
|
slave_top := SLAVE_TOP_BRAM;
|
|
|
|
elsif std_match(top_decode, "11--") then
|
|
|
|
slave_top := SLAVE_TOP_IO;
|
|
|
|
end if;
|
|
|
|
|
|
|
|
-- Top level wishbone muxing.
|
|
|
|
wb_bram_in <= wb_master_out;
|
|
|
|
wb_bram_in.cyc <= '0';
|
|
|
|
wb_dram_in <= wb_master_out;
|
|
|
|
wb_dram_in.cyc <= '0';
|
|
|
|
wb_io_in <= wb_master_out;
|
|
|
|
wb_io_in.cyc <= '0';
|
|
|
|
case slave_top is
|
|
|
|
when SLAVE_TOP_BRAM =>
|
|
|
|
wb_bram_in.cyc <= wb_master_out.cyc;
|
|
|
|
wb_master_in <= wb_bram_out;
|
|
|
|
when SLAVE_TOP_DRAM =>
|
|
|
|
if HAS_DRAM then
|
|
|
|
wb_dram_in.cyc <= wb_master_out.cyc;
|
|
|
|
wb_master_in <= wb_dram_out;
|
|
|
|
else
|
|
|
|
wb_master_in.ack <= wb_master_out.cyc and wb_master_out.stb;
|
|
|
|
wb_master_in.dat <= (others => '1');
|
|
|
|
wb_master_in.stall <= '0';
|
|
|
|
end if;
|
|
|
|
when SLAVE_TOP_IO =>
|
|
|
|
wb_io_in.cyc <= wb_master_out.cyc;
|
|
|
|
wb_master_in <= wb_io_out;
|
|
|
|
end case;
|
|
|
|
|
|
|
|
end process slave_top_intercon;
|
|
|
|
|
|
|
|
-- IO wishbone slave 64->32 bits converter
|
|
|
|
--
|
|
|
|
-- For timing reasons, this adds a one cycle latch on the way both
|
|
|
|
-- in and out. This relaxes timing and routing pressure on the "main"
|
|
|
|
-- memory bus by moving all simple IOs to a slower 32-bit bus.
|
|
|
|
--
|
|
|
|
-- This implementation is rather dumb at the moment, no stash buffer,
|
|
|
|
-- so we stall whenever that latch is busy. This can be improved.
|
|
|
|
--
|
|
|
|
slave_io_latch: process(system_clk)
|
|
|
|
-- State
|
|
|
|
type state_t is (IDLE, WAIT_ACK_BOT, WAIT_ACK_TOP);
|
|
|
|
variable state : state_t;
|
|
|
|
|
|
|
|
-- Misc
|
|
|
|
variable has_top : boolean;
|
|
|
|
variable has_bot : boolean;
|
|
|
|
begin
|
|
|
|
if rising_edge(system_clk) then
|
|
|
|
if (rst) then
|
|
|
|
state := IDLE;
|
|
|
|
wb_io_out.ack <= '0';
|
|
|
|
wb_io_out.stall <= '0';
|
|
|
|
wb_sio_out.cyc <= '0';
|
|
|
|
wb_sio_out.stb <= '0';
|
|
|
|
has_top := false;
|
|
|
|
has_bot := false;
|
|
|
|
else
|
|
|
|
case state is
|
|
|
|
when IDLE =>
|
|
|
|
-- Clear ACK in case it was set
|
|
|
|
wb_io_out.ack <= '0';
|
|
|
|
|
|
|
|
-- Do we have a cycle ?
|
|
|
|
if wb_io_in.cyc = '1' and wb_io_in.stb = '1' then
|
|
|
|
-- Stall master until we are done, we are't (yet) pipelining
|
|
|
|
-- this, it's all slow IOs.
|
|
|
|
wb_io_out.stall <= '1';
|
|
|
|
|
|
|
|
-- Start cycle downstream
|
|
|
|
wb_sio_out.cyc <= '1';
|
|
|
|
wb_sio_out.stb <= '1';
|
|
|
|
|
|
|
|
-- Copy write enable to IO out, copy address as well
|
|
|
|
wb_sio_out.we <= wb_io_in.we;
|
|
|
|
wb_sio_out.adr <= wb_io_in.adr(wb_sio_out.adr'left downto 3) & "000";
|
|
|
|
|
|
|
|
-- Do we have a top word and/or a bottom word ?
|
|
|
|
has_top := wb_io_in.sel(7 downto 4) /= "0000";
|
|
|
|
has_bot := wb_io_in.sel(3 downto 0) /= "0000";
|
|
|
|
|
|
|
|
-- If we have a bottom word, handle it first, otherwise
|
|
|
|
-- send the top word down. XXX Split the actual mux out
|
|
|
|
-- and only generate a control signal.
|
|
|
|
if has_bot then
|
|
|
|
if wb_io_in.we = '1' then
|
|
|
|
wb_sio_out.dat <= wb_io_in.dat(31 downto 0);
|
|
|
|
end if;
|
|
|
|
wb_sio_out.sel <= wb_io_in.sel(3 downto 0);
|
|
|
|
|
|
|
|
-- Wait for ack
|
|
|
|
state := WAIT_ACK_BOT;
|
|
|
|
else
|
|
|
|
if wb_io_in.we = '1' then
|
|
|
|
wb_sio_out.dat <= wb_io_in.dat(63 downto 32);
|
|
|
|
end if;
|
|
|
|
wb_sio_out.sel <= wb_io_in.sel(7 downto 4);
|
|
|
|
|
|
|
|
-- Bump address
|
|
|
|
wb_sio_out.adr(2) <= '1';
|
|
|
|
|
|
|
|
-- Wait for ack
|
|
|
|
state := WAIT_ACK_TOP;
|
|
|
|
end if;
|
|
|
|
end if;
|
|
|
|
when WAIT_ACK_BOT =>
|
|
|
|
-- If we aren't stalled by the device, clear stb
|
|
|
|
if wb_sio_in.stall = '0' then
|
|
|
|
wb_sio_out.stb <= '0';
|
|
|
|
end if;
|
|
|
|
|
|
|
|
-- Handle ack
|
|
|
|
if wb_sio_in.ack = '1' then
|
|
|
|
-- If it's a read, latch the data
|
|
|
|
if wb_sio_out.we = '0' then
|
|
|
|
wb_io_out.dat(31 downto 0) <= wb_sio_in.dat;
|
|
|
|
end if;
|
|
|
|
|
|
|
|
-- Do we have a "top" part as well ?
|
|
|
|
if has_top then
|
|
|
|
-- Latch data & sel
|
|
|
|
if wb_io_in.we = '1' then
|
|
|
|
wb_sio_out.dat <= wb_io_in.dat(63 downto 32);
|
|
|
|
end if;
|
|
|
|
wb_sio_out.sel <= wb_io_in.sel(7 downto 4);
|
|
|
|
|
|
|
|
-- Bump address and set STB
|
|
|
|
wb_sio_out.adr(2) <= '1';
|
|
|
|
wb_sio_out.stb <= '1';
|
|
|
|
|
|
|
|
-- Wait for new ack
|
|
|
|
state := WAIT_ACK_TOP;
|
|
|
|
else
|
|
|
|
-- We are done, ack up, clear cyc downstram
|
|
|
|
wb_sio_out.cyc <= '0';
|
|
|
|
|
|
|
|
-- And ack & unstall upstream
|
|
|
|
wb_io_out.ack <= '1';
|
|
|
|
wb_io_out.stall <= '0';
|
|
|
|
|
|
|
|
-- Wait for next one
|
|
|
|
state := IDLE;
|
|
|
|
end if;
|
|
|
|
end if;
|
|
|
|
when WAIT_ACK_TOP =>
|
|
|
|
-- If we aren't stalled by the device, clear stb
|
|
|
|
if wb_sio_in.stall = '0' then
|
|
|
|
wb_sio_out.stb <= '0';
|
|
|
|
end if;
|
|
|
|
|
|
|
|
-- Handle ack
|
|
|
|
if wb_sio_in.ack = '1' then
|
|
|
|
-- If it's a read, latch the data
|
|
|
|
if wb_sio_out.we = '0' then
|
|
|
|
wb_io_out.dat(63 downto 32) <= wb_sio_in.dat;
|
|
|
|
end if;
|
|
|
|
|
|
|
|
-- We are done, ack up, clear cyc downstram
|
|
|
|
wb_sio_out.cyc <= '0';
|
|
|
|
|
|
|
|
-- And ack & unstall upstream
|
|
|
|
wb_io_out.ack <= '1';
|
|
|
|
wb_io_out.stall <= '0';
|
|
|
|
|
|
|
|
-- Wait for next one
|
|
|
|
state := IDLE;
|
|
|
|
end if;
|
|
|
|
end case;
|
|
|
|
end if;
|
|
|
|
end if;
|
|
|
|
end process;
|
|
|
|
|
|
|
|
-- IO wishbone slave intercon.
|
|
|
|
--
|
|
|
|
slave_io_intercon: process(wb_sio_out, wb_syscon_out, wb_uart0_out, wb_uart1_out,
|
|
|
|
wb_ext_io_out, wb_xics_icp_out, wb_xics_ics_out,
|
|
|
|
wb_spiflash_out)
|
|
|
|
variable slave_io : slave_io_type;
|
|
|
|
|
|
|
|
variable match : std_ulogic_vector(31 downto 12);
|
|
|
|
variable ext_valid : boolean;
|
|
|
|
begin
|
|
|
|
|
|
|
|
-- Simple address decoder.
|
|
|
|
slave_io := SLAVE_IO_NONE;
|
|
|
|
match := "11" & wb_sio_out.adr(29 downto 12);
|
|
|
|
if std_match(match, x"FF---") and HAS_DRAM then
|
|
|
|
slave_io := SLAVE_IO_EXTERNAL;
|
|
|
|
elsif std_match(match, x"F----") then
|
|
|
|
slave_io := SLAVE_IO_SPI_FLASH_MAP;
|
|
|
|
elsif std_match(match, x"C0000") then
|
|
|
|
slave_io := SLAVE_IO_SYSCON;
|
|
|
|
elsif std_match(match, x"C0002") then
|
|
|
|
slave_io := SLAVE_IO_UART;
|
|
|
|
elsif std_match(match, x"C0003") then
|
|
|
|
slave_io := SLAVE_IO_UART1;
|
|
|
|
elsif std_match(match, x"C8---") then
|
|
|
|
slave_io := SLAVE_IO_EXTERNAL;
|
|
|
|
elsif std_match(match, x"C0004") then
|
|
|
|
slave_io := SLAVE_IO_ICP;
|
|
|
|
elsif std_match(match, x"C0005") then
|
|
|
|
slave_io := SLAVE_IO_ICS;
|
|
|
|
elsif std_match(match, x"C0006") then
|
|
|
|
slave_io := SLAVE_IO_SPI_FLASH_REG;
|
|
|
|
end if;
|
|
|
|
slave_io_dbg <= slave_io;
|
|
|
|
wb_uart0_in <= wb_sio_out;
|
|
|
|
wb_uart0_in.cyc <= '0';
|
|
|
|
wb_uart1_in <= wb_sio_out;
|
|
|
|
wb_uart1_in.cyc <= '0';
|
|
|
|
wb_spiflash_in <= wb_sio_out;
|
|
|
|
wb_spiflash_in.cyc <= '0';
|
|
|
|
wb_spiflash_is_reg <= '0';
|
|
|
|
wb_spiflash_is_map <= '0';
|
|
|
|
|
|
|
|
-- Only give xics 8 bits of wb addr (for now...)
|
|
|
|
wb_xics_icp_in <= wb_sio_out;
|
|
|
|
wb_xics_icp_in.adr <= (others => '0');
|
|
|
|
wb_xics_icp_in.adr(7 downto 0) <= wb_sio_out.adr(7 downto 0);
|
|
|
|
wb_xics_icp_in.cyc <= '0';
|
|
|
|
wb_xics_ics_in <= wb_sio_out;
|
|
|
|
wb_xics_ics_in.adr <= (others => '0');
|
|
|
|
wb_xics_ics_in.adr(11 downto 0) <= wb_sio_out.adr(11 downto 0);
|
|
|
|
wb_xics_ics_in.cyc <= '0';
|
|
|
|
|
|
|
|
wb_ext_io_in <= wb_sio_out;
|
|
|
|
wb_ext_io_in.cyc <= '0';
|
|
|
|
|
|
|
|
wb_syscon_in <= wb_sio_out;
|
|
|
|
wb_syscon_in.cyc <= '0';
|
|
|
|
|
|
|
|
wb_ext_is_dram_csr <= '0';
|
|
|
|
wb_ext_is_dram_init <= '0';
|
|
|
|
wb_ext_is_eth <= '0';
|
|
|
|
|
|
|
|
-- Default response, ack & return all 1's
|
|
|
|
wb_sio_in.dat <= (others => '1');
|
|
|
|
wb_sio_in.ack <= wb_sio_out.stb and wb_sio_out.cyc;
|
|
|
|
wb_sio_in.stall <= '0';
|
|
|
|
|
|
|
|
case slave_io is
|
|
|
|
when SLAVE_IO_EXTERNAL =>
|
|
|
|
-- Ext IO "chip selects"
|
|
|
|
--
|
|
|
|
-- DRAM init is special at 0xFF* so we just test the top
|
|
|
|
-- bit. Everything else is at 0xC8* so we test only bits
|
|
|
|
-- 23 downto 16.
|
|
|
|
--
|
|
|
|
ext_valid := false;
|
|
|
|
if wb_sio_out.adr(29) = '1' and HAS_DRAM then -- DRAM init is special
|
|
|
|
wb_ext_is_dram_init <= '1';
|
|
|
|
ext_valid := true;
|
|
|
|
elsif wb_sio_out.adr(23 downto 16) = x"00" and HAS_DRAM then
|
|
|
|
wb_ext_is_dram_csr <= '1';
|
|
|
|
ext_valid := true;
|
|
|
|
elsif wb_sio_out.adr(23 downto 16) = x"02" and HAS_LITEETH then
|
|
|
|
wb_ext_is_eth <= '1';
|
|
|
|
ext_valid := true;
|
|
|
|
elsif wb_sio_out.adr(23 downto 16) = x"03" and HAS_LITEETH then
|
|
|
|
wb_ext_is_eth <= '1';
|
|
|
|
ext_valid := true;
|
|
|
|
end if;
|
|
|
|
if ext_valid then
|
|
|
|
wb_ext_io_in.cyc <= wb_sio_out.cyc;
|
|
|
|
wb_sio_in <= wb_ext_io_out;
|
|
|
|
end if;
|
|
|
|
|
|
|
|
when SLAVE_IO_SYSCON =>
|
|
|
|
wb_syscon_in.cyc <= wb_sio_out.cyc;
|
|
|
|
wb_sio_in <= wb_syscon_out;
|
|
|
|
when SLAVE_IO_UART =>
|
|
|
|
wb_uart0_in.cyc <= wb_sio_out.cyc;
|
|
|
|
wb_sio_in <= wb_uart0_out;
|
|
|
|
when SLAVE_IO_ICP =>
|
|
|
|
wb_xics_icp_in.cyc <= wb_sio_out.cyc;
|
|
|
|
wb_sio_in <= wb_xics_icp_out;
|
|
|
|
when SLAVE_IO_ICS =>
|
|
|
|
wb_xics_ics_in.cyc <= wb_sio_out.cyc;
|
|
|
|
wb_sio_in <= wb_xics_ics_out;
|
|
|
|
when SLAVE_IO_UART1 =>
|
|
|
|
wb_uart1_in.cyc <= wb_sio_out.cyc;
|
|
|
|
wb_sio_in <= wb_uart1_out;
|
|
|
|
when SLAVE_IO_SPI_FLASH_MAP =>
|
|
|
|
-- Clear top bits so they don't make their way to the
|
|
|
|
-- fash chip.
|
|
|
|
wb_spiflash_in.adr(29 downto 28) <= "00";
|
|
|
|
wb_spiflash_in.cyc <= wb_sio_out.cyc;
|
|
|
|
wb_sio_in <= wb_spiflash_out;
|
|
|
|
wb_spiflash_is_map <= '1';
|
|
|
|
when SLAVE_IO_SPI_FLASH_REG =>
|
|
|
|
wb_spiflash_in.cyc <= wb_sio_out.cyc;
|
|
|
|
wb_sio_in <= wb_spiflash_out;
|
|
|
|
wb_spiflash_is_reg <= '1';
|
|
|
|
when others =>
|
|
|
|
end case;
|
|
|
|
|
|
|
|
end process;
|
|
|
|
|
|
|
|
-- Syscon slave
|
|
|
|
syscon0: entity work.syscon
|
|
|
|
generic map(
|
|
|
|
HAS_UART => true,
|
|
|
|
HAS_DRAM => HAS_DRAM,
|
|
|
|
BRAM_SIZE => MEMORY_SIZE,
|
|
|
|
DRAM_SIZE => DRAM_SIZE,
|
|
|
|
DRAM_INIT_SIZE => DRAM_INIT_SIZE,
|
|
|
|
CLK_FREQ => CLK_FREQ,
|
|
|
|
HAS_SPI_FLASH => HAS_SPI_FLASH,
|
|
|
|
SPI_FLASH_OFFSET => SPI_FLASH_OFFSET,
|
|
|
|
HAS_LITEETH => HAS_LITEETH,
|
|
|
|
UART0_IS_16550 => UART0_IS_16550,
|
|
|
|
HAS_UART1 => HAS_UART1
|
|
|
|
)
|
|
|
|
port map(
|
|
|
|
clk => system_clk,
|
|
|
|
rst => rst,
|
|
|
|
wishbone_in => wb_syscon_in,
|
|
|
|
wishbone_out => wb_syscon_out,
|
|
|
|
dram_at_0 => dram_at_0,
|
|
|
|
core_reset => do_core_reset,
|
|
|
|
soc_reset => open -- XXX TODO
|
|
|
|
);
|
|
|
|
|
|
|
|
--
|
|
|
|
-- UART0
|
|
|
|
--
|
|
|
|
-- Either potato (legacy) or 16550
|
|
|
|
--
|
|
|
|
uart0_pp: if not UART0_IS_16550 generate
|
|
|
|
uart0: entity work.pp_soc_uart
|
|
|
|
generic map(
|
|
|
|
FIFO_DEPTH => 32
|
|
|
|
)
|
|
|
|
port map(
|
|
|
|
clk => system_clk,
|
|
|
|
reset => rst_uart,
|
|
|
|
txd => uart0_txd,
|
|
|
|
rxd => uart0_rxd,
|
|
|
|
irq => uart0_irq,
|
|
|
|
wb_adr_in => wb_uart0_in.adr(11 downto 0),
|
|
|
|
wb_dat_in => wb_uart0_in.dat(7 downto 0),
|
|
|
|
wb_dat_out => uart0_dat8,
|
|
|
|
wb_cyc_in => wb_uart0_in.cyc,
|
|
|
|
wb_stb_in => wb_uart0_in.stb,
|
|
|
|
wb_we_in => wb_uart0_in.we,
|
|
|
|
wb_ack_out => wb_uart0_out.ack
|
|
|
|
);
|
|
|
|
end generate;
|
|
|
|
|
|
|
|
uart0_16550 : if UART0_IS_16550 generate
|
|
|
|
signal irq_l : std_ulogic;
|
|
|
|
begin
|
|
|
|
uart0: uart_top
|
|
|
|
port map (
|
|
|
|
wb_clk_i => system_clk,
|
|
|
|
wb_rst_i => rst_uart,
|
|
|
|
wb_adr_i => wb_uart0_in.adr(4 downto 2),
|
|
|
|
wb_dat_i => wb_uart0_in.dat(7 downto 0),
|
|
|
|
wb_dat_o => uart0_dat8,
|
|
|
|
wb_we_i => wb_uart0_in.we,
|
|
|
|
wb_stb_i => wb_uart0_in.stb,
|
|
|
|
wb_cyc_i => wb_uart0_in.cyc,
|
|
|
|
wb_ack_o => wb_uart0_out.ack,
|
|
|
|
int_o => irq_l,
|
|
|
|
stx_pad_o => uart0_txd,
|
|
|
|
srx_pad_i => uart0_rxd,
|
|
|
|
rts_pad_o => open,
|
|
|
|
cts_pad_i => '1',
|
|
|
|
dtr_pad_o => open,
|
|
|
|
dsr_pad_i => '1',
|
|
|
|
ri_pad_i => '0',
|
|
|
|
dcd_pad_i => '1'
|
|
|
|
);
|
|
|
|
|
|
|
|
-- Add a register on the irq out, helps timing
|
|
|
|
uart0_irq_latch: process(system_clk)
|
|
|
|
begin
|
|
|
|
if rising_edge(system_clk) then
|
|
|
|
uart0_irq <= irq_l;
|
|
|
|
end if;
|
|
|
|
end process;
|
|
|
|
end generate;
|
|
|
|
|
|
|
|
wb_uart0_out.dat <= x"000000" & uart0_dat8;
|
|
|
|
wb_uart0_out.stall <= not wb_uart0_out.ack;
|
|
|
|
|
|
|
|
--
|
|
|
|
-- UART1
|
|
|
|
--
|
|
|
|
-- Always 16550 if it exists
|
|
|
|
--
|
|
|
|
uart1: if HAS_UART1 generate
|
|
|
|
signal irq_l : std_ulogic;
|
|
|
|
begin
|
|
|
|
uart1: uart_top
|
|
|
|
port map (
|
|
|
|
wb_clk_i => system_clk,
|
|
|
|
wb_rst_i => rst_uart,
|
|
|
|
wb_adr_i => wb_uart1_in.adr(4 downto 2),
|
|
|
|
wb_dat_i => wb_uart1_in.dat(7 downto 0),
|
|
|
|
wb_dat_o => uart1_dat8,
|
|
|
|
wb_we_i => wb_uart1_in.we,
|
|
|
|
wb_stb_i => wb_uart1_in.stb,
|
|
|
|
wb_cyc_i => wb_uart1_in.cyc,
|
|
|
|
wb_ack_o => wb_uart1_out.ack,
|
|
|
|
int_o => irq_l,
|
|
|
|
stx_pad_o => uart1_txd,
|
|
|
|
srx_pad_i => uart1_rxd,
|
|
|
|
rts_pad_o => open,
|
|
|
|
cts_pad_i => '1',
|
|
|
|
dtr_pad_o => open,
|
|
|
|
dsr_pad_i => '1',
|
|
|
|
ri_pad_i => '0',
|
|
|
|
dcd_pad_i => '1'
|
|
|
|
);
|
|
|
|
-- Add a register on the irq out, helps timing
|
|
|
|
uart0_irq_latch: process(system_clk)
|
|
|
|
begin
|
|
|
|
if rising_edge(system_clk) then
|
|
|
|
uart1_irq <= irq_l;
|
|
|
|
end if;
|
|
|
|
end process;
|
|
|
|
wb_uart1_out.dat <= x"000000" & uart1_dat8;
|
|
|
|
wb_uart1_out.stall <= not wb_uart1_out.ack;
|
|
|
|
end generate;
|
|
|
|
|
|
|
|
no_uart1 : if not HAS_UART1 generate
|
|
|
|
wb_uart1_out.dat <= x"00000000";
|
|
|
|
wb_uart1_out.ack <= wb_uart1_in.cyc and wb_uart1_in.stb;
|
|
|
|
wb_uart1_out.stall <= '0';
|
|
|
|
end generate;
|
|
|
|
|
|
|
|
spiflash_gen: if HAS_SPI_FLASH generate
|
|
|
|
spiflash: entity work.spi_flash_ctrl
|
|
|
|
generic map (
|
|
|
|
DATA_LINES => SPI_FLASH_DLINES,
|
|
|
|
DEF_CLK_DIV => SPI_FLASH_DEF_CKDV,
|
|
|
|
DEF_QUAD_READ => SPI_FLASH_DEF_QUAD
|
|
|
|
)
|
|
|
|
port map(
|
|
|
|
rst => rst_spi,
|
|
|
|
clk => system_clk,
|
|
|
|
wb_in => wb_spiflash_in,
|
|
|
|
wb_out => wb_spiflash_out,
|
|
|
|
wb_sel_reg => wb_spiflash_is_reg,
|
|
|
|
wb_sel_map => wb_spiflash_is_map,
|
|
|
|
sck => spi_flash_sck,
|
|
|
|
cs_n => spi_flash_cs_n,
|
|
|
|
sdat_o => spi_flash_sdat_o,
|
|
|
|
sdat_oe => spi_flash_sdat_oe,
|
|
|
|
sdat_i => spi_flash_sdat_i
|
|
|
|
);
|
|
|
|
end generate;
|
|
|
|
|
|
|
|
no_spi0_gen: if not HAS_SPI_FLASH generate
|
|
|
|
wb_spiflash_out.dat <= (others => '1');
|
|
|
|
wb_spiflash_out.ack <= wb_spiflash_in.cyc and wb_spiflash_in.stb;
|
|
|
|
wb_spiflash_out.stall <= wb_spiflash_in.cyc and not wb_spiflash_out.ack;
|
|
|
|
end generate;
|
|
|
|
|
|
|
|
xics_icp: entity work.xics_icp
|
|
|
|
port map(
|
|
|
|
clk => system_clk,
|
|
|
|
rst => rst_xics,
|
|
|
|
wb_in => wb_xics_icp_in,
|
|
|
|
wb_out => wb_xics_icp_out,
|
|
|
|
ics_in => ics_to_icp,
|
|
|
|
core_irq_out => core_ext_irq
|
|
|
|
);
|
|
|
|
|
|
|
|
xics_ics: entity work.xics_ics
|
|
|
|
generic map(
|
|
|
|
SRC_NUM => 16,
|
|
|
|
PRIO_BITS => 3
|
|
|
|
)
|
|
|
|
port map(
|
|
|
|
clk => system_clk,
|
|
|
|
rst => rst_xics,
|
|
|
|
wb_in => wb_xics_ics_in,
|
|
|
|
wb_out => wb_xics_ics_out,
|
|
|
|
int_level_in => int_level_in,
|
|
|
|
icp_out => ics_to_icp
|
|
|
|
);
|
|
|
|
|
|
|
|
-- Assign external interrupts
|
|
|
|
interrupts: process(all)
|
|
|
|
begin
|
|
|
|
int_level_in <= (others => '0');
|
|
|
|
int_level_in(0) <= uart0_irq;
|
|
|
|
int_level_in(1) <= ext_irq_eth;
|
|
|
|
int_level_in(2) <= uart1_irq;
|
|
|
|
end process;
|
|
|
|
|
|
|
|
-- BRAM Memory slave
|
|
|
|
bram: if MEMORY_SIZE /= 0 generate
|
|
|
|
bram0: entity work.wishbone_bram_wrapper
|
|
|
|
generic map(
|
|
|
|
MEMORY_SIZE => MEMORY_SIZE,
|
|
|
|
RAM_INIT_FILE => RAM_INIT_FILE
|
|
|
|
)
|
|
|
|
port map(
|
|
|
|
clk => system_clk,
|
|
|
|
rst => rst_bram,
|
|
|
|
wishbone_in => wb_bram_in,
|
|
|
|
wishbone_out => wb_bram_out
|
|
|
|
);
|
|
|
|
end generate;
|
|
|
|
|
|
|
|
no_bram: if MEMORY_SIZE = 0 generate
|
|
|
|
wb_bram_out.ack <= wb_bram_in.cyc and wb_bram_in.stb;
|
|
|
|
wb_bram_out.dat <= x"FFFFFFFFFFFFFFFF";
|
|
|
|
wb_bram_out.stall <= not wb_bram_out.ack;
|
|
|
|
end generate;
|
|
|
|
|
|
|
|
-- DMI(debug bus) <-> JTAG bridge
|
|
|
|
dtm: entity work.dmi_dtm
|
|
|
|
generic map(
|
|
|
|
ABITS => 8,
|
|
|
|
DBITS => 64
|
|
|
|
)
|
|
|
|
port map(
|
|
|
|
sys_clk => system_clk,
|
|
|
|
sys_reset => rst_dtm,
|
|
|
|
dmi_addr => dmi_addr,
|
|
|
|
dmi_din => dmi_din,
|
|
|
|
dmi_dout => dmi_dout,
|
|
|
|
dmi_req => dmi_req,
|
|
|
|
dmi_wr => dmi_wr,
|
|
|
|
dmi_ack => dmi_ack
|
|
|
|
);
|
|
|
|
|
|
|
|
-- DMI interconnect
|
|
|
|
dmi_intercon: process(dmi_addr, dmi_req,
|
|
|
|
dmi_wb_ack, dmi_wb_dout,
|
|
|
|
dmi_core_ack, dmi_core_dout)
|
|
|
|
|
|
|
|
-- DMI address map (each address is a full 64-bit register)
|
|
|
|
--
|
|
|
|
-- Offset: Size: Slave:
|
|
|
|
-- 0 4 Wishbone
|
|
|
|
-- 10 16 Core
|
|
|
|
|
|
|
|
type slave_type is (SLAVE_WB,
|
|
|
|
SLAVE_CORE,
|
|
|
|
SLAVE_NONE);
|
|
|
|
variable slave : slave_type;
|
|
|
|
begin
|
|
|
|
-- Simple address decoder
|
|
|
|
slave := SLAVE_NONE;
|
|
|
|
if std_match(dmi_addr, "000000--") then
|
|
|
|
slave := SLAVE_WB;
|
|
|
|
elsif std_match(dmi_addr, "0001----") then
|
|
|
|
slave := SLAVE_CORE;
|
|
|
|
end if;
|
|
|
|
|
|
|
|
-- DMI muxing
|
|
|
|
dmi_wb_req <= '0';
|
|
|
|
dmi_core_req <= '0';
|
|
|
|
case slave is
|
|
|
|
when SLAVE_WB =>
|
|
|
|
dmi_wb_req <= dmi_req;
|
|
|
|
dmi_ack <= dmi_wb_ack;
|
|
|
|
dmi_din <= dmi_wb_dout;
|
|
|
|
when SLAVE_CORE =>
|
|
|
|
dmi_core_req <= dmi_req;
|
|
|
|
dmi_ack <= dmi_core_ack;
|
|
|
|
dmi_din <= dmi_core_dout;
|
|
|
|
when others =>
|
|
|
|
dmi_ack <= dmi_req;
|
|
|
|
dmi_din <= (others => '1');
|
|
|
|
end case;
|
|
|
|
|
|
|
|
-- SIM magic exit
|
|
|
|
if SIM and dmi_req = '1' and dmi_addr = "11111111" and dmi_wr = '1' then
|
|
|
|
stop;
|
|
|
|
end if;
|
|
|
|
end process;
|
|
|
|
|
|
|
|
-- Wishbone debug master (TODO: Add a DMI address decoder)
|
|
|
|
wishbone_debug: entity work.wishbone_debug_master
|
|
|
|
port map(clk => system_clk,
|
|
|
|
rst => rst_wbdb,
|
|
|
|
dmi_addr => dmi_addr(1 downto 0),
|
|
|
|
dmi_dout => dmi_wb_dout,
|
|
|
|
dmi_din => dmi_dout,
|
|
|
|
dmi_wr => dmi_wr,
|
|
|
|
dmi_ack => dmi_wb_ack,
|
|
|
|
dmi_req => dmi_wb_req,
|
|
|
|
wb_in => wishbone_debug_in,
|
|
|
|
wb_out => wishbone_debug_out);
|
|
|
|
|
|
|
|
|
|
|
|
end architecture behaviour;
|