diff --git a/hdl/gn4124core/sim/gn4124_bfm/Manifest.py b/hdl/gn4124core/sim/gn4124_bfm/Manifest.py new file mode 100644 index 0000000000000000000000000000000000000000..a254ae992a97dc33da90aa07d86019e9d234ee52 --- /dev/null +++ b/hdl/gn4124core/sim/gn4124_bfm/Manifest.py @@ -0,0 +1,3 @@ +files = ["mem_model.vhd", "textutil.vhd", "gn412x_bfm.vhd", "util.vhd"] + + diff --git a/hdl/gn4124core/sim/gn4124_bfm/gn4124_bfm.svh b/hdl/gn4124core/sim/gn4124_bfm/gn4124_bfm.svh new file mode 100644 index 0000000000000000000000000000000000000000..518e3993c09b350d89fee59fac3e1cf00d6edbed --- /dev/null +++ b/hdl/gn4124core/sim/gn4124_bfm/gn4124_bfm.svh @@ -0,0 +1,261 @@ +/* Crude wrapper for Gennum-provided GN4124x BFM. Supports only single CSR reads/writes so far. */ + +`ifndef __GN4124_BFM_SVH + `define __GN4124_BFM_SVH 1 + +`include "simdrv_defs.svh" + +interface IGN4124PCIMaster; + + int cmd_str_int[256]; + reg cmd_req = 0; + wire cmd_ack; + + reg internal_rstn = 0; + + wire lclk_p, lclk_n, l2p_clk_p, l2p_clk_n, p2l_clk_p, p2l_clk_n; + wire [15:0] l2p_data, p2l_data; + wire l2p_dframe, l2p_valid, l2p_edb; + wire p2l_dframe, p2l_valid, p2l_rdy; + + + wire [1:0] l_wr_rdy, p_rd_d_rdy; + wire [1:0] p_wr_req, p_wr_rdy, vc_rdy; + wire l2p_rdy, tx_error, rx_error; + wire [15:0] gpio; + + // Local bus to Gennum + modport L2P + ( + + input l2p_clk_n, + input l2p_clk_p, + input l2p_data, + input l2p_dframe, + input l2p_valid, + input l2p_edb, + + output l_wr_rdy, + output p_rd_d_rdy, + output l2p_rdy, + output tx_error + ); + +// Gennum to local bus + modport P2L + ( + output p2l_clk_p, + output p2l_clk_n, + output p2l_dframe, + output p2l_data, + output p2l_valid, + input p2l_rdy, + output p_wr_req, + input p_wr_rdy, + input rx_error, + output vc_rdy + ); + + wire rst_n; + + modport SYS + ( + output lclk_p, + output lclk_n, + output rst_n, + inout gpio); + + + wire [31:0] cmd_rddata; + wire cmd_rddata_valid; + + +GN412X_BFM + U_BFM ( + + .CMD_INT (cmd_str_int), + .CMD_REQ (cmd_req), + .CMD_ACK (cmd_ack), + .CMD_CLOCK_EN (1'b1), + .CMD_RD_DATA(cmd_rddata), + .CMD_RD_DATA_VALID(cmd_rddata_valid), + + .RSTINn (internal_rstn), + .RSTOUT33n(rst_n), + .LCLK (lclk_p), + .LCLKn (lclk_n), + + .L2P_CLKp (l2p_clk_p), + .L2P_CLKn (l2p_clk_n), + + .L2P_DATA (l2p_data), + .L2P_DFRAME (l2p_dframe), + .L2P_VALID (l2p_valid), + .L2P_EDB (l2p_edb), + + + .L_WR_RDY (l_wr_rdy), + .P_RD_D_RDY (p_rd_d_rdy), + .L2P_RDY (l2p_rdy), + .TX_ERROR (tx_error), + + + .P2L_CLKp (p2l_clk_p), + .P2L_CLKn (p2l_clk_n), + + .P2L_DATA (p2l_data), + .P2L_DFRAME (p2l_dframe), + .P2L_VALID (p2l_valid), + .P2L_RDY (p2l_rdy), + .P_WR_REQ (p_wr_req), + .P_WR_RDY (p_wr_rdy), + .RX_ERROR (rx_error), + .VC_RDY (vc_rdy), + + .GPIO (gpio) + ); + + int line_no = 1; + + task send_cmd(string cmd); + int i; + string cmd_2; + + $sformat(cmd_2, "%-1d %s", line_no++, cmd); + +// $display("SendCmd '%s'", cmd_2); + + for(i=0;i<cmd_2.len(); i++) + cmd_str_int[i] = int'(cmd_2[i]); + cmd_str_int[i] = 0; + + #10ns; + cmd_req = 1; + while(!cmd_ack) #1ns; + cmd_req = 0; + while(cmd_ack) #1ns; + #10ns; + + + endtask // send_cmd + + bit ready = 0; + + + task init(); + #100ns; + internal_rstn <= 1; + #100ns; + + send_cmd("init"); + send_cmd("reset %d16"); + send_cmd("bar 0 FF00000000000000 08000000 0 7 0"); + send_cmd("bfm_bar 0 0000000040000000 20000000"); + send_cmd("bfm_bar 1 0000000020000000 20000000"); + send_cmd("wait %d64"); + ready = 1; + + // send_cmd("wr FF000000000A0004 F 007C0270"); +// send_cmd("rd FF000000000A0004 F"); + endtask // init + + initial init(); + + task automatic readback(ref uint64_t value); + @(posedge cmd_rddata_valid); + value = cmd_rddata; + @(negedge cmd_rddata_valid); + endtask // readback + + + +class CBusAccessor_Gennum extends CBusAccessor; + + function new(); + + endfunction // new + + task writem(uint64_t addr[], uint64_t data[], input int size, ref int result); + string cmd; + int i; + + + if(size != 4) + size = 4; + + // $fatal("CBusAccessor_Gennum: only size=4 supported"); + + for(i=0;i<addr.size();i++) + begin + $sformat(cmd,"wr FF000000%08X F %08X", addr[i], data[i]); + send_cmd(cmd); + end + endtask // writem + + task readm(uint64_t addr[], ref uint64_t data[], input int size, ref int result); + string cmd; + int i; + uint64_t tmp; + + + + if(size != 4) + size = 4; + + // $fatal("CBusAccessor_Gennum: only size=4 supported"); + + for(i=0;i<addr.size();i++) + begin + $sformat(cmd,"rd FF000000%08X F", addr[i]); + fork + send_cmd(cmd); + readback(tmp); + join + + data[i] = tmp; + + end + endtask // readm + + +endclass // CBusAccessor_Gennum + + function CBusAccessor get_accessor(); + CBusAccessor_Gennum g = new(); + return g; + endfunction + + + + +endinterface + +/* Helper macro for wiring Gennum-Xilinx ports in spec_top */ + +`define GENNUM_WIRE_SPEC_PINS(IF_NAME) \ + .L_RST_N (IF_NAME.SYS.rst_n),\ +// .L_CLKp (IF_NAME.SYS.lclk_p),\ +// .L_CLKn (IF_NAME.SYS.lclk_n),\ + .p2l_clkp (IF_NAME.P2L.p2l_clk_p),\ + .p2l_clkn (IF_NAME.P2L.p2l_clk_n),\ + .p2l_data (IF_NAME.P2L.p2l_data),\ + .p2l_dframe (IF_NAME.P2L.p2l_dframe),\ + .p2l_valid (IF_NAME.P2L.p2l_valid),\ + .p2l_rdy (IF_NAME.P2L.p2l_rdy),\ + .p_wr_req (IF_NAME.P2L.p_wr_req),\ + .p_wr_rdy (IF_NAME.P2L.p_wr_rdy),\ + .rx_error (IF_NAME.P2L.rx_error),\ + .l2p_clkp (IF_NAME.L2P.l2p_clk_p),\ + .l2p_clkn (IF_NAME.L2P.l2p_clk_n),\ + .l2p_data (IF_NAME.L2P.l2p_data),\ + .l2p_dframe (IF_NAME.L2P.l2p_dframe),\ + .l2p_valid (IF_NAME.L2P.l2p_valid),\ + .l2p_edb (IF_NAME.L2P.l2p_edb),\ + .l2p_rdy (IF_NAME.L2P.l2p_rdy),\ + .l_wr_rdy (IF_NAME.L2P.l_wr_rdy),\ + .p_rd_d_rdy (IF_NAME.L2P.p_rd_d_rdy),\ + .tx_error (IF_NAME.L2P.tx_error),\ + .vc_rdy (IF_NAME.P2L.vc_rdy) + +`endif // `ifndef __GN4124_BFM_SVH + diff --git a/hdl/gn4124core/sim/gn4124_bfm/gn412x_bfm.vhd b/hdl/gn4124core/sim/gn4124_bfm/gn412x_bfm.vhd new file mode 100644 index 0000000000000000000000000000000000000000..578a7470bc240eb873416db8c125ddc15e75b399 --- /dev/null +++ b/hdl/gn4124core/sim/gn4124_bfm/gn412x_bfm.vhd @@ -0,0 +1,2922 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use std.textio.all; + +use work.util.all; +use work.textutil.all; +use work.mem_model.all; + + + +--==========================================================================-- +-- +-- MODULE << gn412x_bfm >> +-- +-- Description : This module generates local bus signals from a text file +-- +-- History: +-- +--==========================================================================-- +-- +-- To Do: +-- Implement rd_outstanding_in +-- Implement gpio for interrupts +-- Implement response_delay + +entity GN412X_BFM is + generic + ( + STRING_MAX : integer := 256; -- Command string maximum length + T_LCLK : time := 10 ns; -- Local Bus Clock Period + T_P2L_CLK_DLY : time := 2 ns; -- Delay from LCLK to P2L_CLK + INSTANCE_LABEL : string := "GN412X_BFM"; -- Label string to be used as a prefix for messages from the model + MODE_PRIMARY : boolean := true -- TRUE for BFM acting as GN412x, FALSE for BFM acting as the DUT + ); + port + ( + --=========================================================-- + ------------------------------------------------------------- + -- CMD_ROUTER Interface + -- + CMD_INT : in t_cmd_array; + CMD_REQ : in bit; + CMD_ACK : out bit; + CMD_CLOCK_EN : in boolean; + CMD_RD_DATA: out std_ulogic_vector(31 downto 0); + CMD_RD_DATA_VALID: out std_logic; + --=========================================================-- + ------------------------------------------------------------- + -- GN412x Signal I/O + ------------------------------------------------------------- + -- This is the reset input to the BFM + -- + RSTINn : in std_logic; + ------------------------------------------------------------- + -- Reset outputs to DUT + -- + RSTOUT18n : out std_logic; + RSTOUT33n : out std_logic; + ------------------------------------------------------------- + ----------------- Local Bus Clock --------------------------- + ------------------------------------------------------------- __ Direction for primary mode + -- / \ + LCLK, LCLKn : inout std_logic; -- Out + ------------------------------------------------------------- + ----------------- Local-to-PCI Dataflow --------------------- + ------------------------------------------------------------- + -- Transmitter Source Synchronous Clock. + -- + L2P_CLKp, L2P_CLKn : inout std_logic; -- In + ------------------------------------------------------------- + -- L2P DDR Link + -- + L2P_DATA : inout std_logic_vector(15 downto 0); -- In -- Parallel Transmit Data. + L2P_DFRAME : inout std_logic; -- In -- Transmit Data Frame. + L2P_VALID : inout std_logic; -- In -- Transmit Data Valid. + L2P_EDB : inout std_logic; -- In -- End-of-Packet Bad Flag. + ------------------------------------------------------------- + -- L2P SDR Controls + -- + L_WR_RDY : inout std_logic_vector(1 downto 0); -- Out -- Local-to-PCIe Write. + P_RD_D_RDY : inout std_logic_vector(1 downto 0); -- Out -- PCIe-to-Local Read Response Data Ready. + L2P_RDY : inout std_logic; -- Out -- Tx Buffer Full Flag. + TX_ERROR : inout std_logic; -- Out -- Transmit Error. + ------------------------------------------------------------- + ----------------- PCIe-to-Local Dataflow --------------------- + ------------------------------------------------------------- + -- Transmitter Source Synchronous Clock. + -- + P2L_CLKp, P2L_CLKn : inout std_logic; -- Out -- P2L Source Synchronous Clock. + ------------------------------------------------------------- + -- P2L DDR Link + -- + P2L_DATA : inout std_logic_vector(15 downto 0); -- Out -- Parallel Receive Data. + P2L_DFRAME : inout std_logic; -- Out -- Receive Frame. + P2L_VALID : inout std_logic; -- Out -- Receive Data Valid. + ------------------------------------------------------------- + -- P2L SDR Controls + -- + P2L_RDY : inout std_logic; -- In -- Rx Buffer Full Flag. + P_WR_REQ : inout std_logic_vector(1 downto 0); -- Out -- PCIe Write Request. + P_WR_RDY : inout std_logic_vector(1 downto 0); -- In -- PCIe Write Ready. + RX_ERROR : inout std_logic; -- In -- Receive Error. + VC_RDY : inout std_logic_vector(1 downto 0); -- Out -- Virtual Channel Ready Status. + ------------------------------------------------------------- + -- GPIO signals + -- + GPIO : inout std_logic_vector(15 downto 0) + ); +end GN412X_BFM; + +architecture MODEL of GN412X_BFM is + + + function f_cmd_to_string (s : t_cmd_array) return string is + variable str : string(1 to STRING_MAX); + variable tmp : integer; + begin + for i in 1 to 256 loop + tmp := s(i); + if(tmp < 0) then + tmp := 0; + elsif(tmp > 255) then + tmp := 255; + end if; + str(i) := character'val(tmp); + end loop; -- i + return str; + end f_cmd_to_string; + + + signal CMD : string(1 to STRING_MAX); +--=========================================================================-- +-- Routine for Data-Comparison +--=========================================================================-- + + procedure read_cmp(L : inout line; PREFIX : in string; DATA, CMP_VAL, MASK : in std_ulogic_vector; ERR : out integer) is + variable X : std_ulogic_vector(DATA'range); + variable STR : string(1 to 32); + begin + X := (others => 'X'); + X := (not MASK and X) or (MASK and CMP_VAL); + STR(1 to X'length) := to_str(X); + if((DATA and MASK) /= (CMP_VAL and MASK)) then + write(L, PREFIX & string'("ERROR @ ")); + write(L, now); + write(L, string'(" : ")); + write(L, LF); + write(L, string'(" Expected => ")); + write(L, STR(1 to X'length)); + + write(L, string'(" (0x")); + write_hex_vector(L, X); + write(L, string'(")")); + + STR(1 to X'length) := to_str(DATA); + write(L, LF); + write(L, string'(" Actually Read => ")); + write(L, STR(1 to X'length)); + + write(L, string'(" (0x")); + write_hex_vector(L, DATA); + write(L, string'(")")); + + ERR := 1; + else + write(L, string'("Read OK @ ")); + write(L, now); + write(L, string'(" : ")); + write(L, LF); + write(L, string'(" Expected => ")); + write(L, STR(1 to X'length)); + ERR := 0; + end if; + end read_cmp; + +--=========================================================================-- +-- Routine to read a boolean from a string +-- +-- it looks for true, false, on, off, 1, 0 +--=========================================================================-- + procedure sget_boolean(S : in string; P : inout integer; X : out boolean) is + variable char : character; + variable q : integer; + begin + if(S'length > P) then + char := S(P); + while(char = ' ') loop -- Skip spaces + P := P + 1; + char := S(P); + end loop; + if(S(P to P+4) = "true ") or (S(P to P+2) = "on ") or (S(P to P+1) = "1 ") then + X := true; + elsif(S(P to P+5) = "false ") or (S(P to P+3) = "off ") or (S(P to P+1) = "0 ") then + X := false; + else + assert false report "ERROR: Couldn't read a boolean" severity error; + end if; + + -- skip over token + while((char /= ' ') and (P < S'length)) loop + char := S(P); + P := P + 1; + end loop; + + else + assert false report "ERROR: Couldn't read a boolean" severity error; + + end if; + + end sget_boolean; + +--=========================================================================-- +-- Produce Another Random Number +-- +-- RNDOUT is a number between MIN and MAX. SR must be stored and fed back each call. +--=========================================================================-- + procedure next_random(SR : inout integer) is + variable SRV : std_ulogic_vector(22 downto 0); + variable SRI : integer; + begin + SRV := To_Vector(SR, SRV'length); + SRV := SRV(21 downto 0) & (SRV(22) xor SRV(17)); + SRI := conv_integer(SRV); + SR := SRI; + end next_random; + +-- function next_random(SR : integer) return integer is +-- variable SRV : std_ulogic_vector(22 downto 0); +-- variable SRI : integer; +-- begin +-- SRV := To_Vector(SR, SRV'length); +-- SRV := SRV(21 downto 0) & (SRV(22) xor SRV(17)); +-- SRI := conv_integer(SRV); +-- return(SRI); +-- end next_random; + +--=========================================================================-- +-- Produce Another Random Number +-- +-- RNDOUT is a number between MIN and MAX. SR must be stored and fed back each call. +--=========================================================================-- + procedure get_random(SR : inout integer; min : in integer; MAX : in integer; RNDOUT : out integer) is + variable SRV : std_ulogic_vector(22 downto 0); + variable SRI : integer; + begin + SRV := To_Vector(SR, SRV'length); + SRV := SRV(21 downto 0) & (SRV(22) xor SRV(17)); + SRI := conv_integer(SRV); + RNDOUT := min + (SRI mod (MAX - min + 1)); + SR := SRI; + end get_random; + +--=========================================================================-- +-- Test a Random Number and test it +-- +-- RNDOUT is a number between MIN and MAX. SR must be stored and fed back each call. +--=========================================================================-- + function test_random(SR : integer; PROBABILITY : integer) return boolean is + begin + return((SR mod 101) < PROBABILITY); + end test_random; + + +--=========================================================================-- +-- Produce a Random Number between MIN and MAX +--=========================================================================-- + function range_random(SR : integer; min : integer; MAX : integer) return integer is + begin + return(min + (SR mod (MAX - min + 1))); + end range_random; + + +--=========================================================================-- +-- Signal Declarations +--=========================================================================-- +----------------------------------------------------------------------------- +-- Global Settings +----------------------------------------------------------------------------- + + constant N_BARS : integer := 2; + constant N_RAM_MAX : integer := 30; -- Maximim size of BFM RAM address = 2**N_RAM_MAX + + constant N_OUTBOUND_RD_OUTSTANDING : integer := 3; -- Maximim number of outstanding reads the BFM will generate + constant N_INBOUND_RD_OUTSTANDING : integer := 3; -- Maximim number of outstanding reads the BFM will accept + constant N_COMPLETION_ID : integer := 4; -- Maximim number of completion IDs + + + ------------------------------------------------------------------------- + -- Settable versions of the generic constants + -- + signal T_LCLKi : time := T_LCLK; -- Local Bus Clock Period + signal T_P2L_CLK_DLYi : time := T_P2L_CLK_DLY; -- Delay from LCLK to P2L_CLK + -- + ------------------------------------------------------------------------- + -- BFM Mode settings + -- + signal PRIMARY : boolean; + signal SECONDARY : boolean; + signal GENERATE_X : boolean := false; + signal EXPECT_ERROR : boolean := false; + signal OUTBOUND_RD_OUTSTANDING : integer := N_OUTBOUND_RD_OUTSTANDING; + signal RESPONSE_DELAY : integer := 0; +-- signal BURST_LENGTH : integer := 0; +-- signal BURST_MODULO : integer := 0; + -- Acceptable values for TYPE are: "0000" for 32-bit memory read + -- Acceptable values for TYPE are: "0001" for 64-bit memory read + -- Acceptable values for TYPE are: "0010" for 32-bit memory write + -- Acceptable values for TYPE are: "0011" for 64-bit memory write + -- Acceptable values for TYPE are: "0100" for Completions without data + -- Acceptable values for TYPE are: "0101" for Completions with data + + signal GPIOi : std_ulogic_vector(GPIO'range); + signal GPIOo : std_ulogic_vector(GPIO'range); + +----------------------------------------------------------------------------- +-- Top Level I/O signals PRIMARY MODE +----------------------------------------------------------------------------- + ------------------------------------------------------------- + ----------------- Local Bus Clock --------------------------- + ------------------------------------------------------------- + -- + signal LCLKo, LCLKno : std_ulogic; + ------------------------------------------------------------- + ----------------- Local-to-PCI Dataflow --------------------- + ------------------------------------------------------------- + -- Transmitter Source Synchronous Clock. + -- + signal L2P_CLKpi, L2P_CLKni : std_ulogic; + signal L2P_CLKi_90 : std_ulogic; + ------------------------------------------------------------- + -- L2P DDR Link + -- + signal L2P_DATAi : std_ulogic_vector(15 downto 0); -- Parallel Transmit Data. + signal L2P_DFRAMEi : std_ulogic; -- Transmit Data Frame. + signal L2P_VALIDi : std_ulogic; -- Transmit Data Valid. + signal L2P_EDBi : std_ulogic; -- End-of-Packet Bad Flag. + ------------------------------------------------------------- + -- L2P SDR Controls + -- + signal L_WR_RDYo : std_ulogic_vector(1 downto 0); -- Local-to-PCIe Write. + signal P_RD_D_RDYo : std_ulogic_vector(1 downto 0); -- PCIe-to-Local Read Response Data Ready. + signal L2P_RDYo : std_ulogic; -- Tx Buffer Full Flag. + signal TX_ERRORo : std_ulogic; -- Transmit Error. + ------------------------------------------------------------- + ----------------- PCIe-to-Local Dataflow --------------------- + ------------------------------------------------------------- + -- Transmitter Source Synchronous Clock. + -- + signal P2L_CLKpo, P2L_CLKno : std_ulogic; -- P2L Source Synchronous Clock. + ------------------------------------------------------------- + -- P2L DDR Link + -- + signal P2L_DATAo : std_ulogic_vector(15 downto 0); -- Parallel Receive Data. + signal P2L_DFRAMEo : std_ulogic; -- Receive Frame. + signal P2L_VALIDo : std_ulogic; -- Receive Data Valid. + ------------------------------------------------------------- + -- P2L SDR Controls + -- + signal P2L_RDYi : std_ulogic; -- Rx Buffer Full Flag. + signal P_WR_REQo : std_ulogic_vector(1 downto 0); -- PCIe Write Request. + signal P_WR_RDYi : std_ulogic_vector(1 downto 0); -- PCIe Write Ready. + signal RX_ERRORi : std_ulogic; -- Receive Error. + signal VC_RDYo : std_ulogic_vector(1 downto 0); -- Virtual Channel Ready Status. + +----------------------------------------------------------------------------- +-- Top Level I/O signals SECONDARY MODE +----------------------------------------------------------------------------- + ------------------------------------------------------------- + ----------------- Local Bus Clock --------------------------- + ------------------------------------------------------------- + -- + signal LCLKi, LCLKni : std_ulogic; + ------------------------------------------------------------- + ----------------- Local-to-PCI Dataflow --------------------- + ------------------------------------------------------------- + -- Transmitter Source Synchronous Clock. + -- + signal L2P_CLKpo, L2P_CLKno : std_ulogic; + ------------------------------------------------------------- + -- L2P DDR Link + -- + signal L2P_DATAo : std_ulogic_vector(15 downto 0); -- Parallel Transmit Data. + signal L2P_DFRAMEo : std_ulogic; -- Transmit Data Frame. + signal L2P_VALIDo : std_ulogic; -- Transmit Data Valid. + signal L2P_EDBo : std_ulogic; -- End-of-Packet Bad Flag. + ------------------------------------------------------------- + -- L2P SDR Controls + -- + signal L_WR_RDYi : std_ulogic_vector(1 downto 0); -- Local-to-PCIe Write. + signal P_RD_D_RDYi : std_ulogic_vector(1 downto 0); -- PCIe-to-Local Read Response Data Ready. + signal L2P_RDYi : std_ulogic; -- Tx Buffer Full Flag. + signal TX_ERRORi : std_ulogic; -- Transmit Error. + ------------------------------------------------------------- + ----------------- PCIe-to-Local Dataflow --------------------- + ------------------------------------------------------------- + -- Transmitter Source Synchronous Clock. + -- + signal P2L_CLKpi, P2L_CLKni : std_ulogic; -- P2L Source Synchronous Clock. + ------------------------------------------------------------- + -- P2L DDR Link + -- + signal P2L_DATAi : std_ulogic_vector(15 downto 0); -- Parallel Receive Data. + signal P2L_DFRAMEi : std_ulogic; -- Receive Frame. + signal P2L_VALIDi : std_ulogic; -- Receive Data Valid. + ------------------------------------------------------------- + -- P2L SDR Controls + -- + signal P2L_RDYo : std_ulogic; -- Rx Buffer Full Flag. + signal P_WR_REQi : std_ulogic_vector(1 downto 0); -- PCIe Write Request. + signal P_WR_RDYo : std_ulogic_vector(1 downto 0); -- PCIe Write Ready. + signal RX_ERRORo : std_ulogic; -- Receive Error. + signal VC_RDYi : std_ulogic_vector(1 downto 0); -- Virtual Channel Ready Status. + + ------------------------------------------------------------- + -- Used by the Inbound State Machine + -- + signal ICLK : std_ulogic; -- Internal Inbound clock + signal LCLK_PERIOD : time := T_LCLK; + +----------------------------------------------------------------------------- +-- Internal Model Signals +----------------------------------------------------------------------------- + type integer_vector is array (natural range <>) of integer; + type boolean_vector is array (natural range <>) of boolean; + ------------------------------------------------------------------------- + -- Largest Cache line size supported + -- + constant N_MAX_LENGTH : integer := 12; + constant MAX_LENGTH : integer := 2**N_MAX_LENGTH; + ------------------------------------------------------------------------- + -- An array of MAX_CACHE * 32 bits + -- + type ARRAY_OF_32 is array (natural range <>) of std_ulogic_vector(31 downto 0); + type ARRAY_OF_2 is array (natural range <>) of std_ulogic_vector(1 downto 0); +-- signal CACHE_BUFFER : ARRAY_OF_32(MAX_CACHE-1 downto 0); + ------------------------------------------------------------------------- + -- + signal CLK0o : std_ulogic; + signal CLK90o : std_ulogic; + signal CLK : std_ulogic; -- This one is used by the main loop + signal RSTOUTo : std_ulogic := '0'; + + + constant T_HOLD_OUT : time := 1 ns; + signal T_HOLD_OUTi : time := T_HOLD_OUT; + + + constant WR_FLUSH_POST : integer := 0; + constant WR_POST : integer := 1; + constant WR_POST_FLUSH : integer := 2; + constant WR_FLUSH_POST_LAST : integer := 3; + + + + constant RD_POST : integer := 0; + constant RD_POST_IPR : integer := 1; + constant RD_IPR_POST : integer := 2; + constant RD_IPR_POST_LAST : integer := 3; + + + + constant MAX_DW_PACKET_SIZE : integer := 1024; -- Maximum packet size in units of DW (32 bits) + + type INT_VECTOR is array (natural range <>) of integer; + type DATA32 is array (natural range <>) of std_ulogic_vector(31 downto 0); + + --======================================================================= + --== + --== Data Structures for INBOUND Read Requests that generate Outbound Completions + --== + --======================================================================= + ------------------------------------------------------------------------- + -- Data Structure used to store inbound read requests + -- note: there are N_COMPLETION_ID size for this data structure rather + -- than N_INBOUND_RD_OUTSTANDING. Only up to N_INBOUND_RD_OUTSTANDING can be active at a time + type INBOUND_READ_REQUEST_TYPE is + record + ADDRESS : std_ulogic_vector(63 downto 0); -- BAR base address + BAR_HIT : std_ulogic_vector(1 downto 0); -- This is for secondary mode + TC : std_ulogic_vector(2 downto 0); -- Trafic class + V : std_ulogic; -- Virtual channel + LENGTH : integer; -- Transfer length in DW + STATE : boolean; -- Used to indicate if the entry is in use or not + end record; + type INBOUND_READ_REQUEST_ARRAY_TYPE is array (N_COMPLETION_ID-1 downto 0) of INBOUND_READ_REQUEST_TYPE; + signal INBOUND_READ_REQUEST_ARRAY : INBOUND_READ_REQUEST_ARRAY_TYPE; + + ------------------------------------------------------------------------- + -- This is the state of the CID for inbound read requests. + -- Used to signal back to the inbound process which completions have been serviced + -- When INBOUND_READ_REQUEST_ARRAY(CID).STATE = INBOUND_READ_REQUEST_CPL_STATE(CID) then the CID may be re-used + signal INBOUND_READ_REQUEST_CPL_STATE : boolean_vector(N_COMPLETION_ID-1 downto 0); + + signal CURRENT_INBOUND_RD_IPR : integer; -- Indicates how many inbound reads are currently outstanding + + + --======================================================================= + --== + --== Data Structures for OUTBOUND Read Requests that result in Inbound Completions + --== + --======================================================================= + -- This data structure is written by the outbound process and + -- read by the inbound process to verify completion packets + type RD_BUFFER_TYPE is + record + ADDRESS : std_ulogic_vector(63 downto 0); + BAR_HIT : std_ulogic_vector(1 downto 0); + FBE : std_ulogic_vector(3 downto 0); + LBE : std_ulogic_vector(3 downto 0); + STATE : boolean; + DATA : DATA32(MAX_DW_PACKET_SIZE-1 downto 0); + MASK : DATA32(MAX_DW_PACKET_SIZE-1 downto 0); + end record; + + type RD_BUFFER_ARRAY_TYPE is array (N_COMPLETION_ID-1 downto 0) of RD_BUFFER_TYPE; + signal RD_BUFFER : RD_BUFFER_ARRAY_TYPE; + + ------------------------------------------------------------------------- + -- Used to signal back to the inbound process which completions have been serviced + -- An entry is in use when OUTBOUND_READ_COMPLETION_STATE(i) /= INBOUND_READ_REQUEST_ARRAY(i).STATE + signal OUTBOUND_READ_REQUEST_CPL_STATE : boolean_vector(N_COMPLETION_ID-1 downto 0); + + signal CURRENT_OUTBOUND_RD_IPR : integer; -- Indicates how many inbound reads are currently outstanding + + + --======================================================================= + --== + --== Data Structures for OUTBOUND Write Requests + --== + --======================================================================= + + -- There is only a single write buffer and it lives as a variable in the outbound process + -- since the inbound process doesn't need to know anything about it + type WR_BUFFER_TYPE is + record + ADDRESS : std_ulogic_vector(63 downto 0); + BE : std_ulogic_vector(3 downto 0); + DATA : std_ulogic_vector(31 downto 0); + end record; + + + type WR_BUFFER_ARRAY_TYPE is array (MAX_DW_PACKET_SIZE-1 downto 0) of WR_BUFFER_TYPE; + +-- signal WR_BUFFER : WR_BUFFER_ARRAY_TYPE; + + signal RANDOM_NUMBER : integer := 0; + + + ------------------------------------------------------------------------- + -- Data Structure used to store info about BARs that generate outbound + -- read/write packets + -- + type BAR_ATTRIBUTE_TYPE is + record + BASE : std_ulogic_vector(63 downto 0); -- BAR base address + MASK : std_ulogic_vector(31 downto 0); -- BAR mask + VC : std_ulogic; -- Virtual Channel + TC : std_ulogic_vector(2 downto 0); -- Trafic class + S : std_ulogic; -- No Snoop Bit + end record; + type BAR_ATTRIBUTE_ARRAY_TYPE is array (1 downto 0) of BAR_ATTRIBUTE_TYPE; + signal BAR_ATTRIBUTE_ARRAY : BAR_ATTRIBUTE_ARRAY_TYPE; + + ------------------------------------------------------------------------- + -- Data Structure used to store info about memory BARs for internal BFM + -- + type BFM_BAR_ATTRIBUTE_TYPE is + record + BASE : std_ulogic_vector(63 downto 0); -- BAR base address + MASK : std_ulogic_vector(31 downto 0); -- BAR mask + end record; + type BFM_BAR_ATTRIBUTE_ARRAY_TYPE is array (1 downto 0) of BFM_BAR_ATTRIBUTE_TYPE; + signal BFM_BAR_ATTRIBUTE_ARRAY : BFM_BAR_ATTRIBUTE_ARRAY_TYPE; + + + + + +-- type VC_SIZE_VECTOR is array (natural range <>) of std_ulogic_vector(1 downto 0); + + +----------------------------------------------------------------------------- +-- Signals used by the outbound and inbound process to read the BFM RAM +----------------------------------------------------------------------------- + signal RAM_REQ0, RAM_REQ1 : boolean := false; -- Request + signal RAM_WR0, RAM_WR1 : boolean; -- Write mode + signal RAM_ACK0, RAM_ACK1 : boolean; -- Acknowledge + signal RAM_ADDR0, RAM_ADDR1 : std_ulogic_vector(N_RAM_MAX-1 downto 0); + signal RAM_WR_DATA0, RAM_WR_DATA1 : std_ulogic_vector(7 downto 0); + signal RAM_RD_DATA0, RAM_RD_DATA1 : std_ulogic_vector(7 downto 0); + +----------------------------------------------------------------------------- +-- Signals Related to the InBound Process +----------------------------------------------------------------------------- + signal IN_DATA : std_ulogic_vector(31 downto 0); + signal IN_DATA_LOW : std_ulogic_vector(15 downto 0); + signal IN_DFRAME : std_ulogic; + signal IN_VALID : std_ulogic; + signal Q_IN_DFRAME : std_ulogic; +----------------------------------------------------------------------------- +-- Signals Related to the OutBound Process +----------------------------------------------------------------------------- + signal Q_OUT_DATA, OUT_DATA : std_ulogic_vector(31 downto 0); + signal Q_OUT_DFRAME, OUT_DFRAME : std_ulogic; + signal Q_OUT_VALID, OUT_VALID : std_ulogic; + signal OUT_WR_REQ : std_ulogic_vector(P_WR_REQ'range); + signal OUT_WR_RDY : std_ulogic_vector(P_WR_RDY'range); + signal Q_OUT_WR_RDY : std_ulogic_vector(P_WR_RDY'range); + + + +--#########################################################################-- +--########################## Start of Code ################################-- +--#########################################################################-- + +begin +CMD <= f_cmd_to_string(CMD_INT); +--=========================================================================-- +-- Reset Outputs +--=========================================================================-- + RSTOUT18n <= not RSTOUTo when PRIMARY else 'Z'; + RSTOUT33n <= not RSTOUTo when PRIMARY else 'Z'; + +--=========================================================================-- +-- Generate the Internal LCLK +--=========================================================================-- + process + begin + CLK0o <= '0'; + CLK90o <= '0'; + loop + if (CMD_CLOCK_EN and PRIMARY) then + CLK0o <= '1'; + CLK0o <= transport '0' after (T_LCLKi/2); + + CLK90o <= transport '1' after (T_LCLKi/4); + CLK90o <= transport '0' after ((T_LCLKi*3)/4); + + wait for T_LCLKi; + else + wait until(CMD_CLOCK_EN'event and CMD_CLOCK_EN and PRIMARY); + end if; + end loop; + end process; + + PRIMARY <= MODE_PRIMARY; + SECONDARY <= not MODE_PRIMARY; + +--=========================================================================-- +-- Top Level I/O signals with timing information +--=========================================================================-- + +--*************************************************************************-- +-- Top Level I/O signals PRIMARY MODE +--*************************************************************************-- + ------------------------------------------------------------- + ----------------- Local Bus Clock --------------------------- + ------------------------------------------------------------- + -- + LCLKo <= CLK0o; + LCLKno <= not CLK0o; + LCLK <= LCLKo when PRIMARY else 'Z'; + LCLKn <= LCLKno when PRIMARY else 'Z'; + ------------------------------------------------------------- + ----------------- Local-to-PCI Dataflow --------------------- + ----------------- (GN412x is an input) --------------------- + ------------------------------------------------------------- + -- Receiver Source Synchronous Clock. + -- + L2P_CLKpi <= To_X01(L2P_CLKp); + L2P_CLKni <= To_X01(L2P_CLKn); + ------------------------------------------------------------- + -- L2P DDR Link + -- + L2P_DATAi <= To_X01(To_StdULogicVector(L2P_DATA)); + L2P_DFRAMEi <= To_X01(L2P_DFRAME); + L2P_VALIDi <= To_X01(L2P_VALID); + L2P_EDBi <= To_X01(L2P_EDB); + ------------------------------------------------------------- + -- L2P SDR Controls + -- + L_WR_RDY <= To_StdLogicVector(L_WR_RDYo) after T_HOLD_OUTi when PRIMARY else (others => 'Z'); + P_RD_D_RDY <= To_StdLogicVector(P_RD_D_RDYo) after T_HOLD_OUTi when PRIMARY else (others => 'Z'); + L2P_RDY <= L2P_RDYo after T_HOLD_OUTi when PRIMARY else 'Z'; + TX_ERROR <= TX_ERRORo after T_HOLD_OUTi when PRIMARY else 'Z'; + ------------------------------------------------------------- + ----------------- PCIe-to-Local Dataflow --------------------- + ----------------- (GN412x is an output) --------------------- + ------------------------------------------------------------- + -- Transmitter Source Synchronous Clock. + -- + P2L_CLKp <= P2L_CLKpo when PRIMARY else 'Z'; + P2L_CLKn <= P2L_CLKno when PRIMARY else 'Z'; + P2L_CLKpo <= transport not CLK0o after T_P2L_CLK_DLYi when PRIMARY else 'Z'; -- Note that P2L_CLK is effectively inverted + P2L_CLKno <= transport CLK0o after T_P2L_CLK_DLYi when PRIMARY else 'Z'; + ------------------------------------------------------------- + -- P2L DDR Link + -- + P2L_DATA <= transport To_StdLogicVector(P2L_DATAo) after T_P2L_CLK_DLYi when PRIMARY else (others => 'Z'); + P2L_DFRAME <= transport P2L_DFRAMEo after T_P2L_CLK_DLYi when PRIMARY else 'Z'; + P2L_VALID <= transport P2L_VALIDo after T_P2L_CLK_DLYi when PRIMARY else 'Z'; + ------------------------------------------------------------- + -- P2L SDR Controls + -- + P2L_RDYi <= To_X01(P2L_RDY); + P_WR_REQ <= To_StdLogicVector(P_WR_REQo) when PRIMARY else (others => 'Z'); + P_WR_RDYi <= To_X01(To_StdULogicVector(P_WR_RDY)); + RX_ERRORi <= To_X01(RX_ERROR); + VC_RDY <= To_StdLogicVector(VC_RDYo) when PRIMARY else (others => 'Z'); + + CLK <= CLK0o when PRIMARY else LCLK; + +----------------------------------------------------------------------------- +-- PRIMARY MODE Internal Signals +----------------------------------------------------------------------------- + process + begin + wait until(CLK90o'event and (CLK90o = '1')); + P2L_DATAo <= Q_OUT_DATA(15 downto 0); + P2L_DFRAMEo <= Q_OUT_DFRAME; + P2L_VALIDo <= Q_OUT_VALID; + P_WR_REQo <= OUT_WR_REQ; + if(RSTOUTo = '1') then + VC_RDYo <= (others => '0'); + else + VC_RDYo <= (others => '1'); + end if; + + wait until(CLK90o'event and (CLK90o = '0')); + P2L_DATAo <= Q_OUT_DATA(31 downto 16); + end process; + + + L_WR_RDYo <= (others => '1'); + P_RD_D_RDYo <= (others => '1'); + L2P_RDYo <= '1'; + TX_ERRORo <= '0'; + + GPIO <= To_StdLogicVector(GPIOo); + GPIOo <= (others => 'Z'); + GPIOi <= To_StdULogicVector(GPIO); + + +--*************************************************************************-- +-- Top Level I/O signals SECONDARY MODE +--*************************************************************************-- + ------------------------------------------------------------- + ----------------- Local-to-PCI Dataflow --------------------- + ----------------- (GN412x is an output) --------------------- + ------------------------------------------------------------- + -- Transmitter Source Synchronous Clock. + -- + L2P_CLKp <= transport L2P_CLKpo after T_P2L_CLK_DLYi when SECONDARY else 'Z'; + L2P_CLKn <= transport L2P_CLKno after T_P2L_CLK_DLYi when SECONDARY else 'Z'; + ------------------------------------------------------------- + -- L2P DDR Link + -- + L2P_DATA <= transport To_StdLogicVector(L2P_DATAo) after T_P2L_CLK_DLYi when SECONDARY else (others => 'Z'); + L2P_DFRAME <= transport L2P_DFRAMEo after T_P2L_CLK_DLYi when SECONDARY else 'Z'; + L2P_VALID <= transport L2P_VALIDo after T_P2L_CLK_DLYi when SECONDARY else 'Z'; + L2P_EDB <= transport L2P_EDBo after T_P2L_CLK_DLYi when SECONDARY else 'Z'; + ------------------------------------------------------------- + -- L2P SDR Controls + -- + L_WR_RDYi <= To_X01(To_StdULogicVector(L_WR_RDY)); + P_RD_D_RDYi <= To_X01(To_StdULogicVector(P_RD_D_RDY)); + L2P_RDYi <= To_X01(L2P_RDY); + TX_ERRORi <= To_X01(TX_ERROR); + + ------------------------------------------------------------- + ----------------- PCIe-to-Local Dataflow --------------------- + ----------------- (GN412x is an input) --------------------- + ------------------------------------------------------------- + -- Receiver Source Synchronous Clock. + -- + P2L_CLKpi <= To_X01(P2L_CLKp); + P2L_CLKni <= To_X01(P2L_CLKn); + ------------------------------------------------------------- + -- P2L DDR Link + -- + P2L_DATAi <= To_X01(To_StdULogicVector(P2L_DATA)); + P2L_DFRAMEi <= To_X01(P2L_DFRAME); + P2L_VALIDi <= To_X01(P2L_VALID); + ------------------------------------------------------------- + -- P2L SDR Controls + -- + P2L_RDY <= 'Z' when PRIMARY else P2L_RDYo after T_HOLD_OUTi; + P_WR_REQi <= To_X01(To_StdULogicVector(P_WR_REQ)); -- INPUT + P_WR_RDY <= (others => 'Z') when PRIMARY else To_StdLogicVector(P_WR_RDYo) after T_HOLD_OUTi; -- OUTPUT + VC_RDYi <= To_X01(To_StdULogicVector(VC_RDY)); -- INPUT + RX_ERROR <= 'Z' when PRIMARY else RX_ERRORo after T_HOLD_OUTi; -- OUTPUT + +----------------------------------------------------------------------------- +-- SECONDARY MODE Internal Signals +----------------------------------------------------------------------------- + L2P_CLKpo <= CLK; + L2P_CLKno <= not CLK; + + L2P_DATAo <= OUT_DATA(15 downto 0) when (L2P_CLKpo = '1') else OUT_DATA(31 downto 16); + L2P_DFRAMEo <= OUT_DFRAME; + L2P_VALIDo <= OUT_VALID; + L2P_EDBo <= '0'; -- CHANGE: Add L2P_EDB functionality + + + + +--*************************************************************************-- +-- Signals Related to the OutBound Process +--*************************************************************************-- + process + begin + wait until(CLK0o'event and (CLK0o = '1')); + Q_OUT_DATA <= OUT_DATA; + Q_OUT_DFRAME <= OUT_DFRAME; + Q_OUT_VALID <= OUT_VALID; + end process; + + process + begin + wait until(CLK'event and (CLK = '1')); + if(PRIMARY) then + Q_OUT_WR_RDY <= P_WR_RDYi; + else + Q_OUT_WR_RDY <= L_WR_RDYi; + end if; + OUT_WR_RDY <= Q_OUT_WR_RDY; + end process; + + + +--*************************************************************************-- +-- Signals Related to the InBound Process +--*************************************************************************-- + ICLK <= not L2P_CLKpi after T_LCLKi/4 when PRIMARY else not P2L_CLKpi; + +-- process -- Determine LCLK period for when in secondary mode +-- variable DELTA : time; +-- begin +-- wait until(LCLKi'event and (LCLKi = '1')); +-- DELTA := NOW; +-- wait until(LCLKi'event and (LCLKi = '1')); +-- DELTA := NOW - DELTA; +-- LCLK_PERIOD <= DELTA; +-- end process; + + process + begin + wait until(ICLK'event and (ICLK = '0')); + if(PRIMARY) then + IN_DATA_LOW <= L2P_DATAi; + else + IN_DATA_LOW <= P2L_DATAi; + end if; + wait until(ICLK'event and (ICLK = '1')); + if(IN_VALID = '1') then + Q_IN_DFRAME <= IN_DFRAME; + end if; + end process; + + IN_DATA(31 downto 16) <= L2P_DATAi when PRIMARY else P2L_DATAi; + IN_DATA(15 downto 0) <= IN_DATA_LOW; + + IN_DFRAME <= L2P_DFRAMEi when PRIMARY else P2L_DFRAMEi; + IN_VALID <= L2P_VALIDi when PRIMARY else P2L_VALIDi; + +-- CHANGE: add ability to de-assert RDY + + P_WR_RDYo <= P_WR_REQi; + +--*************************************************************************-- +-- Signals involving interaction between InBound/Outbound Processes +--*************************************************************************-- +----------------------------------------------------------------------------- +-- Keep track of how many inbound reads requests are in progress +----------------------------------------------------------------------------- + process(INBOUND_READ_REQUEST_ARRAY, INBOUND_READ_REQUEST_CPL_STATE) + variable COUNT : integer; + begin + COUNT := 0; + for I in 0 to N_COMPLETION_ID-1 loop + if(INBOUND_READ_REQUEST_ARRAY(I).STATE /= INBOUND_READ_REQUEST_CPL_STATE(I)) then + COUNT := COUNT + 1; + end if; + end loop; + CURRENT_INBOUND_RD_IPR <= COUNT; + end process; + + +----------------------------------------------------------------------------- +-- Keep track of how many outbound reads requests are in progress +----------------------------------------------------------------------------- + process(RD_BUFFER, OUTBOUND_READ_REQUEST_CPL_STATE) + variable COUNT : integer; + begin + COUNT := 0; + for I in 0 to N_COMPLETION_ID-1 loop + if(RD_BUFFER(I).STATE /= OUTBOUND_READ_REQUEST_CPL_STATE(I)) then + COUNT := COUNT + 1; + end if; + end loop; + CURRENT_OUTBOUND_RD_IPR <= COUNT; + end process; + + +--#########################################################################-- +-- +-- OutBound State Machine +-- +-- (Handles TX from the BFM: P2L for Primary and L2P for secondary) +-- +--#########################################################################-- + process + --file OUT_FILE : text is out "STD_OUTPUT"; + file OUT_FILE : text open write_mode is "NullFile"; + variable OUTPUT_LINE : line; + variable ERR_CNT : integer; + variable L_CMD : string(1 to 80); + variable TMP_STR : string(1 to 80); + variable QCMD : string(CMD'range); + variable L_NUM : integer; + variable L_ADDR : std_ulogic_vector(63 downto 0); + variable L_BE : std_ulogic_vector(3 downto 0); + variable L_DATA : std_ulogic_vector(31 downto 0); + variable MODULO_MASK : std_ulogic_vector(31 downto 2); + variable L_MASK : std_ulogic_vector(31 downto 0); + variable AMASK_TMP : std_ulogic_vector(31 downto 0); -- Will become 63:0 + variable CHAR_PTR : integer; + variable I_TMP : integer; + variable I_TMP2 : integer; + variable I_TMP3 : integer; + variable I_TMP4 : integer; + variable I_TMP5 : integer; + variable vERR : integer; + variable B_TMP : boolean; + variable START : time; + variable N_BURST_MODULO : INT_VECTOR(0 to N_BARS-1); + variable BURST_LENGTH : INT_VECTOR(0 to N_BARS-1); + variable CURRENT_BAR : integer; + variable CURRENT_VC : integer; + variable IWAIT_RANDOM : integer; + variable IWAIT_RANDOM_N : integer; + variable RNDNUM : integer; + variable BAR_HIT : boolean_vector(1 downto 0); + variable BFM_BAR_HIT : boolean_vector(1 downto 0); + variable DATA_TMP8 : std_ulogic_vector(7 downto 0); + variable DATA_TMP32 : std_ulogic_vector(31 downto 0); + variable A_TMP : std_ulogic_vector(L_ADDR'range); + variable RD_ADDRESS : std_ulogic_vector(L_ADDR'range); + + -- + -- Write Buffer Management + variable WR_BUFFER_COUNT : integer; + variable WR_BUFFER_PTR : integer; + variable WR_CASE : integer; + variable RW_SEQUENTIAL : boolean; + variable WR_BUFFER : WR_BUFFER_ARRAY_TYPE; + variable WR_TYPE : std_ulogic_vector(3 downto 0); + + -- + -- Read Request Buffer Management +-- variable RD_BUFFER_COUNT : integer; + variable RD_BUFFER_PTR : integer; + variable CURRENT_CID : integer; -- Current read completion ID + variable NEXT_CID : integer; -- Next read completion ID + variable RD_CASE : integer; + variable RD_TYPE : std_ulogic_vector(3 downto 0); + + variable RW_BLAST : boolean; + -- + -- OUT variables + variable OUT_NOSNOOP : std_ulogic; +-- variable OUT_VC : std_ulogic; + variable OUT_TC : std_ulogic_vector(2 downto 0); + + -- + -- Read Completion Management + variable CPL_MODULO : integer; -- Modulo boundary for + variable CPL_ORDER : boolean; + variable CPL_LENGTH : integer; + variable CPL_LAST : std_ulogic; + variable CPL_CID : integer; + variable CPL_POP_PTR : INT_VECTOR(N_COMPLETION_ID-1 downto 0); -- DW pointer for each CID + + variable IWAIT_RND_SEED : integer; + + + variable DEBUG : integer; + + + begin + + -- Signal Initialization + OUT_DATA <= (others => 'Z'); + OUT_DFRAME <= '0'; + OUT_VALID <= '0'; + OUT_WR_REQ <= (others => '0'); + + + -- Variable Initialization + ERR_CNT := 0; + WR_BUFFER_COUNT := 0; + WR_BUFFER_PTR := 0; + RD_BUFFER_PTR := 0; + CURRENT_BAR := 0; + BURST_LENGTH := (others => 512); + NEXT_CID := N_COMPLETION_ID - 1; + CURRENT_CID := N_COMPLETION_ID - 1; + DEBUG := 0; + CPL_POP_PTR := (others => 0); + CPL_CID := 0; + CPL_MODULO := 64; + INBOUND_READ_REQUEST_CPL_STATE <= (others => false); + + loop + CMD_ACK <= '0'; + + wait on CMD_REQ, CLK; + + --============================================================-- + -- Load in a command + --============================================================-- + if(CMD_REQ = '1') then + + START := now; +-- if (CMD_REQ /= '1') then +-- wait until (CMD_REQ'EVENT and CMD_REQ = '1'); +-- end if; + + QCMD := CMD; + CMD_ACK <= '1'; + if (CMD_REQ /= '0') then + wait until (CMD_REQ'event and CMD_REQ = '0'); + end if; + + if (START /= now) then + wait until(CLK'event and (CLK = '1')); + end if; + + + CHAR_PTR := 1; -- Point to beginning of line + +-- report "Q_CMD " & QCMD; + + SGET_TOKEN(QCMD, CHAR_PTR, L_CMD); -- Strip off line number + +-- report "Line " & L_CMD; + + SGET_TOKEN(QCMD, CHAR_PTR, L_CMD); -- Load the command + +-- report "Command " & L_CMD; + + + if(not MODE_PRIMARY) then + DEBUG := DEBUG + 1; + end if; + + --============================================================-- + -- Command Decode -- + --============================================================-- + + --*------------------------------------------------------------- + --* wr ADDR BE DATA + --* + --* - handles the wr/wrb command + --* + --* Write Cases: + --* + --* Single Buffer Write Action + --* Write Empty Burstable Taken + --* + --* N N N FLUSH, POST : WR_CASE=WR_FLUSH_POST + --* N N Y POST : WR_CASE=WR_POST + --* N Y - POST : WR_CASE=WR_POST + --* Y N N FLUSH, POST, LAST: WR_CASE=WR_FLUSH_POST_LAST + --* Y N Y POST, FLUSH : WR_CASE=WR_POST_FLUSH + --* Y Y - POST, FLUSH : WR_CASE=WR_POST_FLUSH + --* + --* + --*------------------------------------------------------------- + +-- report "L_CMD" & L_CMD; + + if (L_CMD(1 to 2) = "wr") or (L_CMD(1 to 3) = "rd ") or (L_CMD(1 to 3) = "rdb") then + SGET_VECTOR_64(QCMD, CHAR_PTR, L_ADDR); + SGET_VECTOR(QCMD, CHAR_PTR, L_BE); + SGET_VECTOR(QCMD, CHAR_PTR, L_DATA); + if(L_CMD(1 to 2) = "rd") then + while(QCMD(CHAR_PTR) = ' ') loop -- Skip spaces + CHAR_PTR := CHAR_PTR + 1; + if(CHAR_PTR > QCMD'length) then + exit; + end if; + end loop; + if(CHAR_PTR > QCMD'length) then + L_MASK := (others => '0'); + else + SGET_VECTOR(QCMD, CHAR_PTR, L_MASK); + end if; + end if; + + BAR_HIT(0) := (((L_ADDR(31 downto 0) and BAR_ATTRIBUTE_ARRAY(0).MASK) = BAR_ATTRIBUTE_ARRAY(0).BASE(31 downto 0)) + and (L_ADDR(63 downto 32) = BAR_ATTRIBUTE_ARRAY(0).BASE(63 downto 32))); + BAR_HIT(1) := (((L_ADDR(31 downto 0) and BAR_ATTRIBUTE_ARRAY(1).MASK) = BAR_ATTRIBUTE_ARRAY(1).BASE(31 downto 0)) + and (L_ADDR(63 downto 32) = BAR_ATTRIBUTE_ARRAY(1).BASE(63 downto 32))); + + BFM_BAR_HIT(0) := (((L_ADDR(31 downto 0) and BFM_BAR_ATTRIBUTE_ARRAY(0).MASK) = BFM_BAR_ATTRIBUTE_ARRAY(0).BASE(31 downto 0)) + and (L_ADDR(63 downto 32) = BFM_BAR_ATTRIBUTE_ARRAY(0).BASE(63 downto 32))); + BFM_BAR_HIT(1) := (((L_ADDR(31 downto 0) and BFM_BAR_ATTRIBUTE_ARRAY(1).MASK) = BFM_BAR_ATTRIBUTE_ARRAY(1).BASE(31 downto 0)) + and (L_ADDR(63 downto 32) = BFM_BAR_ATTRIBUTE_ARRAY(1).BASE(63 downto 32))); + + if(BAR_HIT(1)) then + CURRENT_BAR := 1; + else + CURRENT_BAR := 0; + end if; + + CURRENT_VC := to_int(BAR_ATTRIBUTE_ARRAY(CURRENT_BAR).VC); + + + if(BAR_HIT(0) or BAR_HIT(1)) then -- Address hits one of the external BARs + + -- Burst-last + + MODULO_MASK := to_vector((((2**N_BURST_MODULO(CURRENT_BAR))/4) - 1), MODULO_MASK'length); + + RW_BLAST := (L_BE /= "1111") + or ((WR_BUFFER_COUNT-1) = (BURST_LENGTH(CURRENT_VC)/4)) + or ((MODULO_MASK and L_ADDR(31 downto 2)) = MODULO_MASK) + or (L_CMD(3) /= 'b'); + + + + --========================================================-- + -- Do Write Cycles + --========================================================-- + if (L_CMD(1 to 2) = "wr") then -- Do Write Cycles + + if(WR_BUFFER_COUNT > 0) then + I_TMP := To_Int(L_ADDR(31 downto 2)); + +-- CHANGE to do proper 64 bit address + + RW_SEQUENTIAL := ((To_Int(WR_BUFFER(WR_BUFFER_PTR-1).ADDRESS(31 downto 2)) + 1) = I_TMP); + + else + RW_SEQUENTIAL := false; + end if; + + if (L_CMD(3) = 'b') then -- See if it will go into the write cache + if (WR_BUFFER_COUNT = 0) then -- Buffer Empty + WR_CASE := WR_POST; + else -- Buffer Not Empty + if RW_SEQUENTIAL and (WR_CASE /= WR_FLUSH_POST_LAST) then + if RW_BLAST then + WR_CASE := WR_POST_FLUSH; -- POST, FLUSH + else + WR_CASE := WR_POST; -- POST + end if; + else + WR_CASE := WR_FLUSH_POST; -- FLUSH, POST + end if; + end if; + else -- Single Write + if(WR_BUFFER_COUNT = 0) then + WR_CASE := WR_POST_FLUSH; + elsif RW_SEQUENTIAL then -- Buffer not Empty and burstable + WR_CASE := WR_POST_FLUSH; + else + WR_CASE := WR_FLUSH_POST_LAST; + end if; + end if; + + --========================================================-- + -- Actualy generate the bus cycles + --========================================================-- + ------------------------------------------------------------ + -- Post to the buffer + ------------------------------------------------------------ + if(WR_CASE = WR_POST) or (WR_CASE = WR_POST_FLUSH) then -- POST + WR_BUFFER(WR_BUFFER_PTR).ADDRESS := L_ADDR; + WR_BUFFER(WR_BUFFER_PTR).BE := L_BE; + WR_BUFFER(WR_BUFFER_PTR).DATA := L_DATA; + WR_BUFFER_COUNT := WR_BUFFER_COUNT + 1; + WR_BUFFER_PTR := WR_BUFFER_PTR + 1; + end if; + + ------------------------------------------------------------ + -- Flush the buffer (write it out to the interface) + ------------------------------------------------------------ + if(WR_CASE = WR_FLUSH_POST) or (WR_CASE = WR_POST_FLUSH) or (WR_CASE = WR_FLUSH_POST_LAST) then -- FLUSH + + -- CHANGE: ADD CHECK FOR PROPER RDY + + + loop + + if(or_reduce(WR_BUFFER(0).ADDRESS(63 downto 32)) = '0') then -- 32 bit address + WR_TYPE := "0010"; + elsif(PRIMARY) then -- Primary mode addresses are always 32 bit + WR_TYPE := "0010"; + else -- 64 bit address + WR_TYPE := "0011"; + end if; + + -------------------------------------------------------- + -- Header Phase + -------------------------------------------------------- + OUT_DATA <= BAR_ATTRIBUTE_ARRAY(CURRENT_BAR).TC -- Bits 31:29 + & BAR_ATTRIBUTE_ARRAY(CURRENT_BAR).S -- Bit 28 + & WR_TYPE -- Bits 27:24 + & WR_BUFFER(WR_BUFFER_COUNT-1).BE -- Bits 23:20 + & WR_BUFFER(0).BE -- Bits 19:16 + & "000" -- Bits 15:13 + & BAR_ATTRIBUTE_ARRAY(CURRENT_BAR).VC -- Bit 12 + & "00" -- Bits 11:10 + & to_vector(WR_BUFFER_COUNT, 10); -- Bits 9:0 + OUT_VALID <= '1'; + OUT_DFRAME <= '1'; + OUT_WR_REQ <= BAR_ATTRIBUTE_ARRAY(CURRENT_BAR).VC & (not BAR_ATTRIBUTE_ARRAY(CURRENT_BAR).VC); + + wait until(CLK'event and (CLK = '1')); + + -- See if we want to insert random wait states + if(IWAIT_RANDOM > 0) then -- Insert wait states + next_random(IWAIT_RND_SEED); + if(test_random(IWAIT_RND_SEED, IWAIT_RANDOM)) then + OUT_VALID <= '0'; + OUT_DATA <= (others => 'X'); + next_random(IWAIT_RND_SEED); + I_TMP := range_random(IWAIT_RND_SEED, 1, IWAIT_RANDOM_N); + for J in 1 to I_TMP loop + wait until(CLK'event and (CLK = '1')); + end loop; + OUT_VALID <= '1'; + end if; + end if; + + + + -------------------------------------------------------- + -- Address Phase + -------------------------------------------------------- + if(WR_TYPE = "0011") then -- Do upper 32 bits of the address + OUT_DATA <= WR_BUFFER(0).ADDRESS(63 downto 32); + wait until(CLK'event and (CLK = '1')); + + + -- See if we want to insert random wait states + if(IWAIT_RANDOM > 0) then -- Insert wait states + next_random(IWAIT_RND_SEED); + if(test_random(IWAIT_RND_SEED, IWAIT_RANDOM)) then + OUT_VALID <= '0'; + OUT_DATA <= (others => 'X'); + next_random(IWAIT_RND_SEED); + I_TMP := range_random(IWAIT_RND_SEED, 1, IWAIT_RANDOM_N); + for J in 1 to I_TMP loop + wait until(CLK'event and (CLK = '1')); + end loop; + OUT_VALID <= '1'; + end if; + end if; + + + end if; + + if(PRIMARY) then + OUT_DATA <= WR_BUFFER(0).ADDRESS(31 downto 2) & '0' & to_mvl(CURRENT_BAR); + else + OUT_DATA <= WR_BUFFER(0).ADDRESS(31 downto 2) & "00"; + end if; + + wait until(CLK'event and (CLK = '1')); + + -------------------------------------------------------- + -- Data Phase + -------------------------------------------------------- + for I in 0 to WR_BUFFER_COUNT-1 loop + + -- See if we want to insert random wait states + if(IWAIT_RANDOM > 0) then -- Insert wait states + next_random(IWAIT_RND_SEED); + if(test_random(IWAIT_RND_SEED, IWAIT_RANDOM)) then + OUT_VALID <= '0'; + OUT_DATA <= (others => 'X'); + next_random(IWAIT_RND_SEED); + I_TMP := range_random(IWAIT_RND_SEED, 1, IWAIT_RANDOM_N); + for J in 1 to I_TMP loop + wait until(CLK'event and (CLK = '1')); + end loop; + OUT_VALID <= '1'; + end if; + end if; + + OUT_DATA <= WR_BUFFER(I).DATA; + if (I = (WR_BUFFER_COUNT-1)) then + OUT_DFRAME <= '0'; + end if; + wait until(CLK'event and (CLK = '1')); + + end loop; + OUT_VALID <= '0'; + OUT_DFRAME <= '0'; + OUT_DATA <= (others => 'Z'); + OUT_WR_REQ <= (others => '0'); + + WR_BUFFER_COUNT := 0; + WR_BUFFER_PTR := 0; + + if(WR_CASE = WR_FLUSH_POST_LAST) then -- The flush must be executed twice + WR_CASE := 99; + WR_BUFFER(WR_BUFFER_PTR).ADDRESS := L_ADDR; + WR_BUFFER(WR_BUFFER_PTR).BE := L_BE; + WR_BUFFER(WR_BUFFER_PTR).DATA := L_DATA; + WR_BUFFER_COUNT := WR_BUFFER_COUNT + 1; + WR_BUFFER_PTR := WR_BUFFER_PTR + 1; + else + exit; + end if; + + end loop; + + end if; + + ------------------------------------------------------------ + -- Post to the buffer + ------------------------------------------------------------ + if(WR_CASE = WR_FLUSH_POST) then --POST + WR_BUFFER(WR_BUFFER_PTR).ADDRESS := L_ADDR; + WR_BUFFER(WR_BUFFER_PTR).BE := L_BE; + WR_BUFFER(WR_BUFFER_PTR).DATA := L_DATA; + WR_BUFFER_COUNT := WR_BUFFER_COUNT + 1; + WR_BUFFER_PTR := WR_BUFFER_PTR + 1; + end if; + + --========================================================-- + -- Do Read Request Cycles + --========================================================-- + --*------------------------------------------------------------- + --* rd ADDR BE DATA MASK + --* + --* - handles the rd/rdb command + --* + --* Read Cases: + --* + --* rdb Buffer Read Action + --* command Empty Burstable Taken + --* + --* N Y - POST, IPR : RD_CASE=RD_POST_IPR* + --* N N Y POST, IPR : RD_CASE=RD_POST_IPR* + --* N N N IPR, POST_LAST : RD_CASE=RD_IPR_POST_LAST* + --* Y Y - POST : RD_CASE=RD_POST* + --* Y N Y POST : RD_CASE=RD_POST* + --* Y N N IPR, POST : RD_CASE=RD_IPR_POST* + --* + --* Note: POST means store the given read into the current CID buffer + --* POST_LAST store the given read into the current CID buffer and mark as LAST (cannot be combined) + --* IPR means set the current buffer state to "In Progress" and load the next CID as current CID + --* NEXT_CID means allocate the next CID and set as current CID + --* + --*------------------------------------------------------------- + else -- do read cycles + + write(OUTPUT_LINE, string'("-- rd(")); + write_hex_vector(OUTPUT_LINE, L_ADDR(63 downto 32)); + write_hex_vector(OUTPUT_LINE, L_ADDR(31 downto 0)); + write(OUTPUT_LINE, string'(", ")); + write_hex_vector(OUTPUT_LINE, L_BE); + write(OUTPUT_LINE, string'(", ")); + write_hex_vector(OUTPUT_LINE, L_DATA); + write(OUTPUT_LINE, string'(", ")); + write_hex_vector(OUTPUT_LINE, L_MASK); + write(OUTPUT_LINE, string'(")")); + writeline(OUT_FILE, OUTPUT_LINE); + + + + if(RD_BUFFER_PTR > 0) then -- current buffer has data + + I_TMP := To_Int(L_ADDR(31 downto 2)); + RW_SEQUENTIAL := (To_Int(RD_ADDRESS(31 downto 2)) = I_TMP); + else + + RD_ADDRESS := L_ADDR; + RW_SEQUENTIAL := false; + end if; + + + + + + if (L_CMD(3) = 'b') then -- command is for a burst + + if (RD_BUFFER_PTR = 0) then -- Buffer Empty + RD_CASE := RD_POST; + else -- Buffer Not Empty + if(RW_SEQUENTIAL and not RW_BLAST) then + RD_CASE := RD_POST; + else + RD_CASE := RD_IPR_POST; + end if; + end if; + else -- Single Write + + if(RD_BUFFER_PTR = 0) then -- Buffer Empty + RD_CASE := RD_POST_IPR; + elsif RW_SEQUENTIAL and (RD_CASE /= RD_IPR_POST_LAST) then -- Buffer not Empty and burstable + RD_CASE := RD_POST_IPR; + else + RD_CASE := RD_IPR_POST_LAST; + end if; + end if; + + + ------------------------------------------------------------ + -- Stall until it is safe to post another buffer + ------------------------------------------------------------ + if(RD_BUFFER_PTR = 0) then + + while(CURRENT_OUTBOUND_RD_IPR >= OUTBOUND_RD_OUTSTANDING) loop -- we must stall + wait until(CLK'event and (CLK = '1')); + end loop; + + -- Allocate a new CID + NEXT_CID := CURRENT_CID; + for I in 0 to N_COMPLETION_ID-1 loop + NEXT_CID := NEXT_CID + 1; + if(NEXT_CID > (N_COMPLETION_ID-1)) then + NEXT_CID := 0; + end if; + if(RD_BUFFER(NEXT_CID).STATE = OUTBOUND_READ_REQUEST_CPL_STATE(NEXT_CID)) then + exit; + end if; + end loop; + CURRENT_CID := NEXT_CID; + + end if; + + + ------------------------------------------------------------ + -- Post to the buffer + ------------------------------------------------------------ + if(RD_CASE = RD_POST_IPR) or (RD_CASE = RD_POST) then -- POST + + if(RD_BUFFER_PTR = 0) then -- buffer empty + RD_BUFFER(CURRENT_CID).ADDRESS <= L_ADDR; + RD_BUFFER(CURRENT_CID).BAR_HIT <= to_mvl(BAR_HIT(1)) & to_mvl(BAR_HIT(1)); + RD_BUFFER(CURRENT_CID).FBE <= L_BE; + end if; + + RD_BUFFER(CURRENT_CID).LBE <= L_BE; + RD_BUFFER(CURRENT_CID).DATA(RD_BUFFER_PTR) <= L_DATA; + RD_BUFFER(CURRENT_CID).MASK(RD_BUFFER_PTR) <= L_MASK; + RD_BUFFER_PTR := RD_BUFFER_PTR + 1; + RD_ADDRESS(RD_ADDRESS'high downto 2) := L_ADDR(RD_ADDRESS'high downto 2) + '1'; + + wait on RD_BUFFER; + + end if; + + ------------------------------------------------------------ + -- Flush the read buffer (send request packet and set the buffer state to in-progress) + ------------------------------------------------------------ + if(RD_CASE = RD_POST_IPR) or (RD_CASE = RD_IPR_POST_LAST) or (RD_CASE = RD_IPR_POST) then -- IPR + RD_BUFFER(CURRENT_CID).STATE <= not RD_BUFFER(CURRENT_CID).STATE; + + -------------------------------------------------------- + -- Do the Read Request + -------------------------------------------------------- + if(or_reduce(RD_BUFFER(CURRENT_CID).ADDRESS(63 downto 32)) = '0') then -- 32 bit address + RD_TYPE := "0000"; + elsif(PRIMARY) then -- Primary mode addresses are always 32 bit + RD_TYPE := "0000"; + else -- 64 bit address + RD_TYPE := "0001"; + end if; + + + -------------------------------------------------------- + -- Header Phase + -------------------------------------------------------- + OUT_DATA <= BAR_ATTRIBUTE_ARRAY(CURRENT_BAR).TC -- Bits 31:29 + & BAR_ATTRIBUTE_ARRAY(CURRENT_BAR).S -- Bit 28 + & RD_TYPE -- Bits 27:24 + & RD_BUFFER(CURRENT_CID).LBE -- Bits 23:20 + & RD_BUFFER(CURRENT_CID).FBE -- Bits 19:16 + & "000" -- Bits 15:13 + & BAR_ATTRIBUTE_ARRAY(CURRENT_BAR).VC -- Bit 12 + & to_vector(CURRENT_CID, 2) -- Bits 11:10 + & to_vector(RD_BUFFER_PTR, 10); -- Bits 9:0 + OUT_VALID <= '1'; + OUT_DFRAME <= '1'; + + wait until(CLK'event and (CLK = '1')); + + + -- See if we want to insert random wait states + if(IWAIT_RANDOM > 0) then -- Insert wait states + next_random(IWAIT_RND_SEED); + if(test_random(IWAIT_RND_SEED, IWAIT_RANDOM)) then + OUT_VALID <= '0'; + OUT_DATA <= (others => 'X'); + next_random(IWAIT_RND_SEED); + I_TMP := range_random(IWAIT_RND_SEED, 1, IWAIT_RANDOM_N); + for J in 1 to I_TMP loop + wait until(CLK'event and (CLK = '1')); + end loop; + OUT_VALID <= '1'; + end if; + end if; + + + -------------------------------------------------------- + -- Address Phase + -------------------------------------------------------- + if(RD_TYPE = "0001") then -- Do upper 32 bits of the address + OUT_DATA <= RD_BUFFER(CURRENT_CID).ADDRESS(63 downto 32); + wait until(CLK'event and (CLK = '1')); + + + -- See if we want to insert random wait states + if(IWAIT_RANDOM > 0) then -- Insert wait states + next_random(IWAIT_RND_SEED); + if(test_random(IWAIT_RND_SEED, IWAIT_RANDOM)) then + OUT_VALID <= '0'; + OUT_DATA <= (others => 'X'); + next_random(IWAIT_RND_SEED); + I_TMP := range_random(IWAIT_RND_SEED, 1, IWAIT_RANDOM_N); + for J in 1 to I_TMP loop + wait until(CLK'event and (CLK = '1')); + end loop; + OUT_VALID <= '1'; + end if; + end if; + + end if; + + if(PRIMARY) then + OUT_DATA <= RD_BUFFER(CURRENT_CID).ADDRESS(31 downto 2) & '0' & to_mvl(CURRENT_BAR); + else + OUT_DATA <= RD_BUFFER(CURRENT_CID).ADDRESS(31 downto 2) & "00"; + end if; + OUT_DFRAME <= '0'; + + wait until(CLK'event and (CLK = '1')); + OUT_VALID <= '0'; + OUT_DATA <= (others => 'Z'); + + + RD_BUFFER_PTR := 0; + + end if; + + ------------------------------------------------------------ + -- Post to the buffer + ------------------------------------------------------------ + + if(RD_CASE = RD_IPR_POST_LAST) or (RD_CASE = RD_IPR_POST) then -- POST + + if(RD_BUFFER_PTR = 0) then -- buffer empty + RD_BUFFER(CURRENT_CID).ADDRESS <= L_ADDR; + RD_BUFFER(CURRENT_CID).FBE <= L_BE; + end if; + + RD_BUFFER(CURRENT_CID).LBE <= L_BE; + RD_BUFFER(CURRENT_CID).DATA(RD_BUFFER_PTR) <= L_DATA; + RD_BUFFER(CURRENT_CID).MASK(RD_BUFFER_PTR) <= L_MASK; + RD_BUFFER_PTR := RD_BUFFER_PTR + 1; + RD_ADDRESS(RD_ADDRESS'high downto 2) := L_ADDR(RD_ADDRESS'high downto 2) + '1'; + + end if; + + + end if; + + + elsif(BFM_BAR_HIT(0) or BFM_BAR_HIT(1)) then -- Address hits one of the internal BFM RAM BARs + + + ------------------------------------------------------------ + -- Write data to the local BFM memory + ------------------------------------------------------------ + A_TMP := L_ADDR(L_ADDR'high downto 2) & "00"; + if (L_CMD(1 to 2) = "wr") then + for i in 0 to L_BE'high loop + + if (L_BE(i) = '1') then + DATA_TMP8 := L_DATA(8*i+7 downto 8*i); + + +write(OUTPUT_LINE, string'("-- Mem_Write(")); +write_hex_vector(OUTPUT_LINE, A_TMP(29 downto 0)); +write(OUTPUT_LINE, string'(", ")); +write_hex_vector(OUTPUT_LINE, DATA_TMP8); +write(OUTPUT_LINE, string'(")")); +writeline(OUT_FILE, OUTPUT_LINE); + + RAM_WR0 <= true; + RAM_ADDR0 <= A_TMP(N_RAM_MAX-1 downto 0); + RAM_WR_DATA0 <= DATA_TMP8; + RAM_REQ0 <= not RAM_REQ0; + wait on RAM_ACK0; + + end if; + A_TMP := A_TMP + '1'; + end loop; +-- report "ENDLOOP"; + ------------------------------------------------------------ + -- Read data from the local BFM memory + ------------------------------------------------------------ + else + for i in 0 to L_BE'high loop + if (L_BE(i) = '1') then + RAM_WR0 <= false; + RAM_ADDR0 <= A_TMP(N_RAM_MAX-1 downto 0); + RAM_REQ0 <= not RAM_REQ0; + wait on RAM_ACK0; + DATA_TMP8 := RAM_RD_DATA0; + +--write(OUTPUT_LINE, string'("-- Mem_Read(")); +--write_hex_vector(OUTPUT_LINE, A_TMP(29 downto 0)); +--write(OUTPUT_LINE, string'(", ")); +--write_hex_vector(OUTPUT_LINE, DATA_TMP8); +--write(OUTPUT_LINE, string'(")")); +--writeline(OUT_FILE, OUTPUT_LINE); + + DATA_TMP32(8*i+7 downto 8*i) := DATA_TMP8; + A_TMP := A_TMP + '1'; + end if; + end loop; + read_cmp(OUTPUT_LINE, (INSTANCE_LABEL & "BFM Local Readback "), DATA_TMP32, L_DATA, L_MASK, vERR); + + + + if(vERR = 1) then writeline(OUT_FILE, OUTPUT_LINE); end if; + + end if; + + else + write(OUTPUT_LINE, "-- ERROR: " & INSTANCE_LABEL); + write(OUTPUT_LINE, NOW, left, 0, ps); + if (L_CMD(1 to 2) = "wr") then + write(OUTPUT_LINE, string'(" The write command didn't match any BAR address :")); + write_hex_vector(OUTPUT_LINE, L_ADDR); + else + write(OUTPUT_LINE, string'(" The read command didn't match any BAR address :")); + write_hex_vector(OUTPUT_LINE, L_ADDR); + end if; + writeline(OUT_FILE, OUTPUT_LINE); + end if; + + --*------------------------------------------------------------- + --* init: Set the model to all the default values + --*------------------------------------------------------------- + elsif (L_CMD(1 to 4) = "init") then + GENERATE_X <= false; + EXPECT_ERROR <= false; + T_LCLKi <= T_LCLK; + T_P2L_CLK_DLYi <= T_P2L_CLK_DLY; + T_HOLD_OUTi <= T_HOLD_OUT; + OUTBOUND_RD_OUTSTANDING <= 1; + RESPONSE_DELAY <= 0; + BURST_LENGTH := (others => 512); + N_BURST_MODULO := (others => log2(512)); + CURRENT_VC := 0; + CPL_MODULO := 64; + IWAIT_RANDOM := 0; + IWAIT_RND_SEED := 5000; + --CURRENT_BAR := 0; + + --*------------------------------------------------------------- + --* reset N: Drive RSTOUT18n, RSTOUT33n for N clocks + --*------------------------------------------------------------- + elsif (L_CMD(1 to 5) = "reset") then + SGET_INT(QCMD, CHAR_PTR, I_TMP); + + RSTOUTo <= '1'; + + if(CMD_CLOCK_EN) then + for i in 1 to I_TMP loop + wait until (CLK'event and CLK = '1'); + end loop; + else + wait for I_TMP * T_LCLKi; + end if; + + RSTOUTo <= '0'; + + --*------------------------------------------------------------- + --* expect_error on|off|true|false: Expect read error + --* + --* When true|on read results will expect a completor error + --* and an ERROR will be reported if it is not + --* + --* When false|off read results will expect the error bit to be + --* off and an ERROR will be reported if it is on + --*------------------------------------------------------------- + elsif (L_CMD(1 to 12) = "expect_error") then + SGET_BOOLEAN(QCMD, CHAR_PTR, B_TMP); + EXPECT_ERROR <= B_TMP; + + --*------------------------------------------------------------- + --* generate_x on|off|true|false: Generate X when signals change + --* + --* When true|on output signals will go X after T_HOLD_OUT from + --* clock when the signal transitions. It will return to a valid + --* logic level after T_CO. + --* + --* CLK ______~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + --* | + --* |<-----------T_CO--------->| + --* | | + --* |<--T_HOLD_OUT-->| | + --* | | + --* SIG ________________________XXXXXXXXXX~~~~~~~ + --* + --*------------------------------------------------------------- + elsif (L_CMD(1 to 10) = "generate_x") then + SGET_BOOLEAN(QCMD, CHAR_PTR, B_TMP); + GENERATE_X <= B_TMP; + + --*------------------------------------------------------------- + --* clk N: set clock period to N ps + --*------------------------------------------------------------- + elsif (L_CMD(1 to 4) = "lclk") then + SGET_INT(QCMD, CHAR_PTR, I_TMP); + T_LCLKi <= I_TMP * 1 ps; + + --*------------------------------------------------------------- + --* iwait_random P N: Initiator random wait state insertion. + --*------------------------------------------------------------- + elsif (L_CMD(1 to 12) = "iwait_random") then + SGET_INT(QCMD, CHAR_PTR, I_TMP); + SGET_INT(QCMD, CHAR_PTR, I_TMP2); + if(I_TMP < 0) or (I_TMP > 100) then + assert false report "WARNING: Specified iwait_random probability is out of the acceptable range of 0-100: setting to 0"; + I_TMP := 0; + end if; + if(I_TMP2 < 0) or (I_TMP2 > 99) then + assert false report "WARNING: Specified iwait_random max wait is out of the acceptable range of 1-32: setting to 1"; + I_TMP2 := 1; + end if; + IWAIT_RANDOM := I_TMP; + IWAIT_RANDOM_N := I_TMP2; + + + + --*------------------------------------------------------------- + --* bfm_bar BAR ADDR: Used to allocate local memory inside + --* the BFM for an external master to R/W. + --*------------------------------------------------------------- + elsif (L_CMD(1 to 7) = "bfm_bar") then + SGET_INT(QCMD, CHAR_PTR, I_TMP); -- BAR + SGET_VECTOR_64(QCMD, CHAR_PTR, L_ADDR); -- ADDR + SGET_INT(QCMD, CHAR_PTR, I_TMP2); -- SIZE + -- Check BAR + if(I_TMP /= 0) then + I_TMP := 1; + end if; + + -- Check SIZE + if(I_TMP2 /= (2**log2(I_TMP2))) then + assert false report "WARNING: Specified SIZE is not a power of 2, rounding down."; + I_TMP2 := (2**log2(I_TMP2)); + end if; + I_TMP2 := I_TMP2 - 1; + AMASK_TMP := to_vector(I_TMP2, AMASK_TMP'length); + AMASK_TMP := not AMASK_TMP; + BFM_BAR_ATTRIBUTE_ARRAY(I_TMP).MASK <= AMASK_TMP; + + -- Check BASE + if(L_ADDR(AMASK_TMP'range) /= (L_ADDR(AMASK_TMP'range) and AMASK_TMP)) then + assert false report "WARNING: Specified Base Address will be alligned to match the BAR size."; + L_ADDR(AMASK_TMP'range) := (L_ADDR(AMASK_TMP'range) and AMASK_TMP); + end if; + BFM_BAR_ATTRIBUTE_ARRAY(I_TMP).BASE <= L_ADDR; + + -- Make sure that BFM_BAR_ATTRIBUTE_ARRAY is updated before continuing + wait until (BFM_BAR_ATTRIBUTE_ARRAY'event); + + write(OUTPUT_LINE, "-- " & INSTANCE_LABEL); + write(OUTPUT_LINE, string'("BFM BAR set to: BAR=")); + write(OUTPUT_LINE, I_TMP); + + write(OUTPUT_LINE, string'(", BASE=0x")); + write_hex_vector(OUTPUT_LINE, BFM_BAR_ATTRIBUTE_ARRAY(I_TMP).BASE); + + write(OUTPUT_LINE, string'(", MASK=0x")); + write_hex_vector(OUTPUT_LINE, BFM_BAR_ATTRIBUTE_ARRAY(I_TMP).MASK); + + writeline(OUT_FILE, OUTPUT_LINE); + + + --*------------------------------------------------------------- + --* bar BAR ADDR SIZE VC TC: Sets up one of the base address + --* registers. All read/write transactions + --* that match a BAR will be routed external. + --*------------------------------------------------------------- + elsif (L_CMD(1 to 3) = "bar") then + SGET_INT(QCMD, CHAR_PTR, I_TMP); -- BAR + SGET_VECTOR_64(QCMD, CHAR_PTR, L_ADDR); -- ADDR + SGET_INT(QCMD, CHAR_PTR, I_TMP2); -- SIZE + SGET_INT(QCMD, CHAR_PTR, I_TMP3); -- VC + SGET_INT(QCMD, CHAR_PTR, I_TMP4); -- TC + SGET_INT(QCMD, CHAR_PTR, I_TMP5); -- S + + -- Check BAR + if(I_TMP /= 0) then + I_TMP := 1; + end if; + + -- Check SIZE + if(I_TMP2 /= (2**log2(I_TMP2))) then + assert false report "WARNING: Specified SIZE is not a power of 2, rounding down."; + I_TMP2 := (2**log2(I_TMP2)); + end if; + I_TMP2 := I_TMP2 - 1; + AMASK_TMP := to_vector(I_TMP2, AMASK_TMP'length); + AMASK_TMP := not AMASK_TMP; + BAR_ATTRIBUTE_ARRAY(I_TMP).MASK <= AMASK_TMP; + + -- Check BASE + if(L_ADDR(AMASK_TMP'range) /= (L_ADDR(AMASK_TMP'range) and AMASK_TMP)) then + assert false report "WARNING: Specified Base Address will be alligned to match the BAR size."; + L_ADDR(AMASK_TMP'range) := (L_ADDR(AMASK_TMP'range) and AMASK_TMP); + end if; + BAR_ATTRIBUTE_ARRAY(I_TMP).BASE <= L_ADDR; + + -- Check VC + if(I_TMP3 < 0) or (I_TMP3 > 1) then + assert false report "WARNING: Specified VC is out of range, will be set to 0."; + I_TMP3 := 0; + end if; + if(I_TMP3 /= 0) then + BAR_ATTRIBUTE_ARRAY(I_TMP).VC <= '1'; + else + BAR_ATTRIBUTE_ARRAY(I_TMP).VC <= '0'; + end if; + + -- Check Trafic Class + if(I_TMP4 < 0) or (I_TMP4 > 7) then + assert false report "WARNING: Specified TC is out of range, will be set to 0."; + I_TMP4 := 0; + end if; + BAR_ATTRIBUTE_ARRAY(I_TMP).TC <= to_vector(I_TMP4, 3); + + -- Check Trafic Class + if(I_TMP5 < 0) or (I_TMP5 > 1) then + assert false report "WARNING: Specified NoSnoop is out of range, will be set to 0."; + I_TMP5 := 0; + end if; + BAR_ATTRIBUTE_ARRAY(I_TMP).S <= to_mvl(I_TMP5); + + + -- Make sure that BAR_ATTRIBUTE_ARRAY is updated before continuing + wait until (BAR_ATTRIBUTE_ARRAY'event); + + write(OUTPUT_LINE, "-- " & INSTANCE_LABEL); + write(OUTPUT_LINE, string'("BAR set to: BAR=")); + write(OUTPUT_LINE, I_TMP); + + write(OUTPUT_LINE, string'(", BASE=0x")); + write_hex_vector(OUTPUT_LINE, BAR_ATTRIBUTE_ARRAY(I_TMP).BASE); + + write(OUTPUT_LINE, string'(", MASK=0x")); + write_hex_vector(OUTPUT_LINE, BAR_ATTRIBUTE_ARRAY(I_TMP).MASK); + + write(OUTPUT_LINE, string'(", VC=")); + write(OUTPUT_LINE, To_bit(BAR_ATTRIBUTE_ARRAY(I_TMP).VC)); + + write(OUTPUT_LINE, string'(", TC=0x")); + write_hex_vector(OUTPUT_LINE, BAR_ATTRIBUTE_ARRAY(I_TMP).TC); + + write(OUTPUT_LINE, string'(", S=")); + write(OUTPUT_LINE, To_bit(BAR_ATTRIBUTE_ARRAY(I_TMP).S)); + + writeline(OUT_FILE, OUTPUT_LINE); + + + + --*------------------------------------------------------------- + --* rd_outstanding_out N: This determines how many outstanding + --* reads the BFM will generate before stalling. + --*------------------------------------------------------------- + elsif (L_CMD(1 to 18) = "rd_outstanding_out") then + SGET_INT(QCMD, CHAR_PTR, I_TMP); -- N + + if(I_TMP < 1) or (I_TMP > 4) then + assert false report "WARNING: Specified reads outstanding is out of the acceptable range of 1-4: setting to 1"; + I_TMP := 1; + end if; + + OUTBOUND_RD_OUTSTANDING <= I_TMP; + + write(OUTPUT_LINE, "-- " & INSTANCE_LABEL); + write(OUTPUT_LINE, string'("rd_outstanding_out set to: ")); + write(OUTPUT_LINE, I_TMP); + writeline(OUT_FILE, OUTPUT_LINE); + + --*------------------------------------------------------------- + --* cpl_modulo N: Sets the burst modulo boundary that + --* read completion packets will get truncated to. + --*------------------------------------------------------------- + elsif (L_CMD(1 to 10) = "cpl_modulo") then + SGET_INT(QCMD, CHAR_PTR, I_TMP); -- N + + if(I_TMP < 8) or (I_TMP > 512) then + assert false report "WARNING: Specified completion modulo is out of the acceptable range of 8-512 bytes: setting to 512 bytes"; + I_TMP := 512; + end if; + + if((2**log2(I_TMP)) /= I_TMP) then + I_TMP := (2**log2(I_TMP)); + write(OUTPUT_LINE, string'("-- WARNING: Specified completion modulo is not a power of 2: setting to ")); + write(OUTPUT_LINE, I_TMP); + write(OUTPUT_LINE, string'(" bytes")); + writeline(OUT_FILE, OUTPUT_LINE); + end if; + CPL_MODULO := I_TMP; + + write(OUTPUT_LINE, "-- " & INSTANCE_LABEL & "cpl_modulo set to: "); + write(OUTPUT_LINE, I_TMP); + write(OUTPUT_LINE, string'(" bytes")); + writeline(OUT_FILE, OUTPUT_LINE); + + --*------------------------------------------------------------- + --* cpl_order N: Completion order: Determines the order of + --* completion packets put out by the BFM. + --*------------------------------------------------------------- + elsif (L_CMD(1 to 9) = "cpl_order") then + SGET_BOOLEAN(QCMD, CHAR_PTR, B_TMP); -- N + + + CPL_ORDER := B_TMP; + + write(OUTPUT_LINE, "-- " & INSTANCE_LABEL); + write(OUTPUT_LINE, string'("cpl_modulo set to: ")); + write(OUTPUT_LINE, I_TMP); + write(OUTPUT_LINE, string'(" bytes")); + writeline(OUT_FILE, OUTPUT_LINE); + + --*------------------------------------------------------------- + --* burst_modulo N BAR: Sets the burst modulo boundary that + --* read/write packets will get truncated to. + --*------------------------------------------------------------- + elsif (L_CMD(1 to 12) = "burst_modulo") then + SGET_INT(QCMD, CHAR_PTR, I_TMP); -- N + SGET_INT(QCMD, CHAR_PTR, I_TMP2); -- BAR + if(I_TMP2 < 0) or (I_TMP2 > (N_BARS-1)) then + assert false report "WARNING: Specified Virtual Channel is out of range, defaulting to 1"; + I_TMP2 := 1; + else + N_BURST_MODULO(I_TMP2) := log2(I_TMP); + end if; + + if(I_TMP < 8) or (I_TMP > 4096) then + assert false report "WARNING: Specified burst modulo is out of the acceptable range of 8-4096 bytes: setting to 512 bytes"; + I_TMP := 512; + end if; + + if((2**log2(I_TMP)) /= I_TMP) then + I_TMP := (2**log2(I_TMP)); + write(OUTPUT_LINE, string'("-- WARNING: Specified burst modulo is not a power of 2: setting to ")); + write(OUTPUT_LINE, I_TMP); + write(OUTPUT_LINE, string'(" bytes")); + writeline(OUT_FILE, OUTPUT_LINE); + end if; + N_BURST_MODULO(I_TMP2) := log2(I_TMP); + + + write(OUTPUT_LINE, "-- " & INSTANCE_LABEL); + write(OUTPUT_LINE, string'("burst_modulo set to: ") & to_str(I_TMP) & " bytes for BAR" & to_str(I_TMP2)); + writeline(OUT_FILE, OUTPUT_LINE); + + + + --*------------------------------------------------------------- + --* burst_length N BAR: Sets the burst length that + --* read/write packets will get truncated to. + --*------------------------------------------------------------- + elsif (L_CMD(1 to 12) = "burst_length") then + SGET_INT(QCMD, CHAR_PTR, I_TMP); -- N + SGET_INT(QCMD, CHAR_PTR, I_TMP2); -- BAR + if(I_TMP2 < 0) or (I_TMP2 > (N_BARS-1)) then + assert false report "WARNING: Specified Virtual Channel is out of range, defaulting to 1"; + I_TMP2 := 1; + end if; + + if(I_TMP < 0) or (I_TMP > 4096) then + assert false report "WARNING: Specified burst length is out of range: setting to 512 bytes"; + BURST_LENGTH(I_TMP2) := 512; + else + BURST_LENGTH(I_TMP2) := I_TMP; + end if; + + write(OUTPUT_LINE, "-- " & INSTANCE_LABEL); + write(OUTPUT_LINE, string'("burst_length set to: ") & to_str(BURST_LENGTH(I_TMP2)) & " for BAR" & to_str(I_TMP2)); + writeline(OUT_FILE, OUTPUT_LINE); + + --*------------------------------------------------------------- + --* wait N: Idle for N clocks + --*------------------------------------------------------------- + elsif (L_CMD(1 to 4) = "wait") then + SGET_INT(QCMD, CHAR_PTR, I_TMP); + + for i in 1 to I_TMP loop + wait until (CLK'event and (CLK = '1')); + end loop; + --*------------------------------------------------------------- + --* gpio_wait N P MASK Wait for N local bus clock intervals for GPIO to reach a defined state. + --*------------------------------------------------------------- + elsif (L_CMD(1 to 9) = "gpio_wait") then + SGET_INT(QCMD, CHAR_PTR, I_TMP); + SGET_VECTOR(QCMD, CHAR_PTR, L_DATA); + SGET_VECTOR(QCMD, CHAR_PTR, L_MASK); + + for i in 1 to I_TMP loop + wait until (CLK'event and (CLK = '1')); + I_TMP2 := i; + -- if(and_reduce(((GPIOi and L_MASK(GPIO'range)) xnor (L_DATA(GPIO'range) and L_MASK(GPIO'range)))) = 0) then + if(((GPIOi and L_MASK(GPIO'range)) = (L_DATA(GPIO'range) and L_MASK(GPIO'range)))) then + exit; + end if; + end loop; + if(I_TMP2 = I_TMP) then -- command timed out + write(OUTPUT_LINE, "-- " & INSTANCE_LABEL & string'("ERROR: gpio_wait command timed out")); + writeline(OUT_FILE, OUTPUT_LINE); + end if; + + + --*------------------------------------------------------------- + --* flush N: Idle for N clocks + --*------------------------------------------------------------- + elsif (L_CMD(1 to 5) = "flush") then + SGET_INT(QCMD, CHAR_PTR, I_TMP); + + for i in 1 to I_TMP loop + if(CURRENT_OUTBOUND_RD_IPR = 0) then + exit; + end if; + wait until (CLK'event and (CLK = '1')); + end loop; + + if(CURRENT_OUTBOUND_RD_IPR /= 0) then + write(OUTPUT_LINE, "-- " & INSTANCE_LABEL); + write(OUTPUT_LINE, string'("ERROR: flush command timed out")); + writeline(OUT_FILE, OUTPUT_LINE); + end if; + + elsif (L_CMD(1 to 2) /= "--") and (L_CMD(1 to 2) /= "//") and (L_CMD(1) /= ' ') then + assert false report "Unrecognized command"; + end if; + + end if; + + --============================================================-- + -- Send out a completion packet + --============================================================-- + if(CURRENT_INBOUND_RD_IPR /= 0) then -- Send out a read completion + + ------------------------------------------------------------ + -- Make sure we are alligned to the clock + ------------------------------------------------------------ + if not(CLK'event and (CLK = '1')) then + wait until (CLK'event and (CLK = '1')); + end if; + + ------------------------------------------------------------ + -- Select the next CID to service (does a round robin and supports OoO) + ------------------------------------------------------------ + + for I in 0 to N_COMPLETION_ID-1 loop + + -- Point to the next possible Completion ID + if(CPL_ORDER or (I /= 0)) then + CPL_CID := CPL_CID + 1; + end if; + if(CPL_CID >= N_COMPLETION_ID) then + CPL_CID := 0; + end if; + + if(INBOUND_READ_REQUEST_ARRAY(CPL_CID).STATE /= INBOUND_READ_REQUEST_CPL_STATE(CPL_CID)) then + exit; + end if; + end loop; + + ------------------------------------------------------------ + -- Header Phase + ------------------------------------------------------------ + + if(INBOUND_READ_REQUEST_ARRAY(CPL_CID).LENGTH = 0) then + RD_TYPE := "0100"; + else + RD_TYPE := "0101"; + end if; + + +-- Start Address = INBOUND_READ_REQUEST_ARRAY(CPL_CID).ADDRESS(31:2) + + -- Start DW Address + + A_TMP := (others => '0'); + A_TMP(RAM_ADDR0'high) := INBOUND_READ_REQUEST_ARRAY(CPL_CID).BAR_HIT(1); + A_TMP(RAM_ADDR0'high-1 downto 2) := INBOUND_READ_REQUEST_ARRAY(CPL_CID).ADDRESS(RAM_ADDR0'high-1 downto 2); + + I_TMP := to_int(A_TMP(RAM_ADDR0'high downto 2)); -- This is the DW base address relative to the BFM internal RAM + I_TMP := I_TMP + CPL_POP_PTR(CPL_CID); -- This is the DW base address + offset + +-- I_TMP := to_int(INBOUND_READ_REQUEST_ARRAY(CPL_CID).BAR_HIT(1) +-- & INBOUND_READ_REQUEST_ARRAY(CPL_CID).ADDRESS(RAM_ADDR0'high-1 downto 2)) +-- + CPL_POP_PTR(CPL_CID); + + + A_TMP := (others => '0'); + A_TMP(RAM_ADDR0'range) := to_vector(I_TMP, RAM_ADDR0'length-2) & "00"; + + CPL_LENGTH := (CPL_MODULO/4) - (I_TMP mod (CPL_MODULO/4)); -- This determines the modulo alligned length + CPL_LENGTH := minimum(INBOUND_READ_REQUEST_ARRAY(CPL_CID).LENGTH - CPL_POP_PTR(CPL_CID), CPL_LENGTH); + + CPL_LAST := to_mvl((CPL_POP_PTR(CPL_CID) + CPL_LENGTH) >= INBOUND_READ_REQUEST_ARRAY(CPL_CID).LENGTH); + + + OUT_DATA <= INBOUND_READ_REQUEST_ARRAY(CPL_CID).TC -- Bits 31:29 (TC) + & '0' -- Bit 28 (unused) + & RD_TYPE -- Bits 27:24 (TYPE) + & "000000" -- Bits 23:18 (unused) + & "00" -- Bits 17:16 (STAT) -- CHANGE to add exception cases + & CPL_LAST -- Bits 15 (L) + & "00" -- Bits 14:13 (unused) + & BAR_ATTRIBUTE_ARRAY(CURRENT_BAR).VC -- Bit 12 + & to_vector(CPL_CID, 2) -- Bits 11:10 + & to_vector(CPL_LENGTH, 10); -- Bits 9:0 + OUT_VALID <= '1'; + OUT_DFRAME <= '1'; + + wait until(CLK'event and (CLK = '1')); + + -- See if we want to insert random wait states + if(IWAIT_RANDOM > 0) then -- Insert wait states + next_random(IWAIT_RND_SEED); + if(test_random(IWAIT_RND_SEED, IWAIT_RANDOM)) then + OUT_VALID <= '0'; + OUT_DATA <= (others => 'X'); + next_random(IWAIT_RND_SEED); + I_TMP := range_random(IWAIT_RND_SEED, 1, IWAIT_RANDOM_N); + for J in 1 to I_TMP loop + wait until(CLK'event and (CLK = '1')); + end loop; + OUT_VALID <= '1'; + end if; + end if; + + + + ------------------------------------------------------------ + -- Data Phase + ------------------------------------------------------------ + + if(INBOUND_READ_REQUEST_ARRAY(CPL_CID).LENGTH = 0) then -- no data to return + OUT_DATA <= (others => 'X'); + wait until(CLK'event and (CLK = '1')); + + else -- send back data + + RAM_WR0 <= false; + + for I in 1 to CPL_LENGTH loop + + for J in 0 to 3 loop -- Do all 4 bytes of the read + + RAM_ADDR0 <= A_TMP(RAM_ADDR0'range); + RAM_REQ0 <= not RAM_REQ0; + + wait on RAM_ACK0; + + OUT_DATA(J*8+7 downto J*8) <= RAM_RD_DATA0; + + A_TMP := A_TMP + '1'; + end loop; + + if(I = CPL_LENGTH) then + OUT_DFRAME <= '0'; + end if; + + wait until(CLK'event and (CLK = '1')); + + CPL_POP_PTR(CPL_CID) := CPL_POP_PTR(CPL_CID) + 1; + + -- See if we want to insert random wait states + if(IWAIT_RANDOM > 0) then -- Insert wait states + next_random(IWAIT_RND_SEED); + if(test_random(IWAIT_RND_SEED, IWAIT_RANDOM)) then + OUT_VALID <= '0'; + OUT_DATA <= (others => 'X'); + next_random(IWAIT_RND_SEED); + I_TMP := range_random(IWAIT_RND_SEED, 1, IWAIT_RANDOM_N); + for J in 1 to I_TMP loop + wait until(CLK'event and (CLK = '1')); + end loop; + OUT_VALID <= '1'; + end if; + end if; + + + + end loop; + + end if; + + if(CPL_LAST = '1') then -- Retire the CID + CPL_POP_PTR(CPL_CID) := 0; + INBOUND_READ_REQUEST_CPL_STATE(CPL_CID) <= INBOUND_READ_REQUEST_ARRAY(CPL_CID).STATE; + wait on INBOUND_READ_REQUEST_CPL_STATE; + end if; + + OUT_VALID <= '0'; + OUT_DFRAME <= '0'; + OUT_DATA <= (others => 'Z'); + + + end if; + + end loop; + end process; + +--#########################################################################-- +-- +-- InBound State Machine +-- +-- (Handles RX to the BFM: L2P for Primary and P2L for secondary) +-- +--#########################################################################-- + process + --file OUT_FILE : text is out "STD_OUTPUT"; + file OUT_FILE : text open write_mode is "NullFile"; + variable OUTPUT_LINE : line; + + variable HEADER_TC : std_ulogic_vector(2 downto 0); + variable HEADER_S : std_ulogic; + variable HEADER_TYPE : std_ulogic_vector(3 downto 0); + variable HEADER_LBE : std_ulogic_vector(3 downto 0); + variable HEADER_FBE : std_ulogic_vector(3 downto 0); + variable HEADER_STAT : std_ulogic_vector(1 downto 0); + variable HEADER_L : std_ulogic; + variable HEADER_V : std_ulogic; + variable HEADER_CID : std_ulogic_vector(1 downto 0); + variable HEADER_LENGTH : std_ulogic_vector(9 downto 0); + variable HEADER : std_ulogic_vector(IN_DATA'range); + variable HEADER_ADDR_LOW : std_ulogic_vector(IN_DATA'range); + variable HEADER_ADDR_HI : std_ulogic_vector(IN_DATA'range); + variable BFM_BAR_HIT : boolean_vector(1 downto 0); + variable RAM_ADDR : std_ulogic_vector(N_RAM_MAX-1 downto 0); + variable RAM_BE : std_ulogic_vector(3 downto 0); + variable RAM_DATA : std_ulogic_vector(7 downto 0); + variable INBOUND_LABEL : string(1 to 3); + variable FIRST_DW : boolean; + variable COMPLETION_ID : integer; -- + variable CID_COUNT : integer; + variable I_HEADER_LENGTH : integer; + variable I_CID : integer; + variable CMP_DATA : std_ulogic_vector(IN_DATA'range); + variable RD_DATA, RD_MASK : std_ulogic_vector(IN_DATA'range); + + variable RD_BUFFER_PTR : INT_VECTOR(N_COMPLETION_ID-1 downto 0); + variable RD_CPL_RAM_ADDR : DATA32(N_COMPLETION_ID-1 downto 0); + variable vERR : integer; + variable TMP_I : integer; + variable TMP32 : std_ulogic_vector(31 downto 0); + + begin + + if(MODE_PRIMARY) then + INBOUND_LABEL := "L2P"; + else + INBOUND_LABEL := "P2L"; + end if; + + COMPLETION_ID := 0; + for I in 0 to N_COMPLETION_ID-1 loop + INBOUND_READ_REQUEST_ARRAY(I).STATE <= false; + RD_CPL_RAM_ADDR(I) := (others => '0'); + end loop; + + RD_BUFFER_PTR := (others => 0); + + loop + CMD_RD_DATA_VALID <= '0'; + + wait until(ICLK'event and (ICLK = '1')); + + if((IN_DFRAME and IN_VALID) = '1') then -- Start of a packet + --*------------------------------------------------------------- + --* Header Phase + --*------------------------------------------------------------- + HEADER := IN_DATA; + HEADER_TC := IN_DATA(31 downto 29); + HEADER_S := IN_DATA(28); + HEADER_TYPE := IN_DATA(27 downto 24); + HEADER_LBE := IN_DATA(23 downto 20); + HEADER_FBE := IN_DATA(19 downto 16); + HEADER_STAT := IN_DATA(17 downto 16); + HEADER_L := IN_DATA(15); + HEADER_V := IN_DATA(12); + HEADER_CID := IN_DATA(11 downto 10); + HEADER_LENGTH := IN_DATA(9 downto 0); + if(Q_IN_DFRAME = '1') then + write(OUTPUT_LINE, "-- ERROR: " & INSTANCE_LABEL); + write(OUTPUT_LINE, NOW); + write(OUTPUT_LINE, ": " & INBOUND_LABEL &" Packet is Improperly Framed (Doesn't start with a L-to-H transition on DFRAME)"); + writeline(OUT_FILE, OUTPUT_LINE); + end if; + + COMPLETION_ID := to_int(HEADER_CID); + + --*------------------------------------------------------------- + --* Address Phase + --*------------------------------------------------------------- + if((HEADER_TYPE = "0000") or (HEADER_TYPE = "0010")) then -- 32 bit address + wait until(ICLK'event and (ICLK = '1') and (IN_VALID = '1')); + HEADER_ADDR_LOW := IN_DATA; + HEADER_ADDR_HI := (others => '0'); + + elsif((HEADER_TYPE = "0001") or (HEADER_TYPE = "0011")) then -- 64 bit address + wait until(ICLK'event and (ICLK = '1') and (IN_VALID = '1')); + HEADER_ADDR_HI := IN_DATA; + wait until(ICLK'event and (ICLK = '1') and (IN_VALID = '1')); + HEADER_ADDR_LOW := IN_DATA; + + elsif(HEADER_TYPE = "0100") then -- Completion: No Data +-- wait until(ICLK'event and (ICLK = '1') and (IN_VALID = '1')); + + elsif(HEADER_TYPE = "0101") then -- Completion: With Data + + else + write(OUTPUT_LINE, "-- ERROR: " & INSTANCE_LABEL); + write(OUTPUT_LINE, NOW); + write(OUTPUT_LINE, string'(" Unrecognized TYPE=0x")); + write_hex_vector(OUTPUT_LINE, HEADER); + write(OUTPUT_LINE, string'(" --> Header Word=0x")); + write_hex_vector(OUTPUT_LINE, HEADER); + writeline(OUT_FILE, OUTPUT_LINE); + end if; + + if(PRIMARY) then + BFM_BAR_HIT(0) := (((HEADER_ADDR_LOW and BFM_BAR_ATTRIBUTE_ARRAY(0).MASK) = BFM_BAR_ATTRIBUTE_ARRAY(0).BASE(31 downto 0)) + and (HEADER_ADDR_HI = BFM_BAR_ATTRIBUTE_ARRAY(0).BASE(63 downto 32))); + BFM_BAR_HIT(1) := (((HEADER_ADDR_LOW and BFM_BAR_ATTRIBUTE_ARRAY(1).MASK) = BFM_BAR_ATTRIBUTE_ARRAY(1).BASE(31 downto 0)) + and (HEADER_ADDR_HI = BFM_BAR_ATTRIBUTE_ARRAY(1).BASE(63 downto 32))); + else + if(HEADER_ADDR_LOW(1 downto 0) = "00") then + BFM_BAR_HIT(0) := true; + BFM_BAR_HIT(1) := false; + elsif(HEADER_ADDR_LOW(1 downto 0) = "01") then + BFM_BAR_HIT(0) := false; + BFM_BAR_HIT(1) := true; + else + BFM_BAR_HIT(0) := false; + BFM_BAR_HIT(1) := false; + end if; + end if; + + RAM_ADDR := to_mvl(BFM_BAR_HIT(1)) & HEADER_ADDR_LOW(RAM_ADDR'high-1 downto 2) & "00"; + RAM_BE := HEADER_FBE; + FIRST_DW := true; + + --*------------------------------------------------------------- + --* Data Phase - Write + --*------------------------------------------------------------- + if((HEADER_TYPE = "0010") or (HEADER_TYPE = "0011")) then -- Write Packet + if(BFM_BAR_HIT(0) or BFM_BAR_HIT(1)) then + L1 : loop + wait until(ICLK'event and (ICLK = '1') and (IN_VALID = '1')); + if(FIRST_DW) then + RAM_BE := HEADER_FBE; + elsif(IN_DFRAME /= '1') then + RAM_BE := HEADER_LBE; + else + RAM_BE := (others => '1'); + end if; + + for i in 0 to RAM_BE'high loop + if (RAM_BE(i) = '1') then + RAM_DATA := IN_DATA(8*i+7 downto 8*i); + + +--write(OUTPUT_LINE, string'("-- " & INSTANCE_LABEL & INBOUND_LABEL &" Mem_Write(")); +--write_hex_vector(OUTPUT_LINE, RAM_ADDR); +--write(OUTPUT_LINE, string'(", ")); +--write_hex_vector(OUTPUT_LINE, RAM_DATA); +--write(OUTPUT_LINE, string'(")")); +--writeline(OUT_FILE, OUTPUT_LINE); + + RAM_WR1 <= true; + RAM_ADDR1 <= RAM_ADDR; + RAM_WR_DATA1 <= RAM_DATA; + RAM_REQ1 <= not RAM_REQ1; + wait on RAM_ACK1; + + end if; + RAM_ADDR := RAM_ADDR + '1'; + end loop; + + FIRST_DW := false; + + if(IN_DFRAME /= '1') then + exit L1; + end if; + end loop; + else + write(OUTPUT_LINE, "-- ERROR: " & INSTANCE_LABEL); + write(OUTPUT_LINE, NOW); + write(OUTPUT_LINE, (" " & INBOUND_LABEL & " Address didn't match any local BAR: HEADER=0x")); + write_hex_vector(OUTPUT_LINE, HEADER); + write(OUTPUT_LINE, string'(", ADDRESS=0x")); + write_hex_vector(OUTPUT_LINE, HEADER_ADDR_HI); + write_hex_vector(OUTPUT_LINE, HEADER_ADDR_LOW); + writeline(OUT_FILE, OUTPUT_LINE); + wait until(ICLK'event and (ICLK = '1') and (IN_VALID = '1') and (IN_DFRAME = '0')); + end if; + + elsif((HEADER_TYPE = "0000") or (HEADER_TYPE = "0001")) then -- Read Request + --*------------------------------------------------------------- + --* Process the Read Request + --*------------------------------------------------------------- + -- INBOUND_READ_REQUEST_ARRAY(i).ADDRESS + -- INBOUND_READ_REQUEST_ARRAY(i).LENGTH + -- INBOUND_READ_REQUEST_ARRAY(i).STATE + -- constant N_INBOUND_RD_OUTSTANDING : integer := 3; -- Maximim number of outstanding reads + -- constant N_COMPLETION_ID : integer := 4; -- Maximim number of completion IDs + + if(INBOUND_READ_REQUEST_CPL_STATE(COMPLETION_ID) /= INBOUND_READ_REQUEST_ARRAY(COMPLETION_ID).STATE) then -- ERROR: CID is in use + write(OUTPUT_LINE, "-- ERROR: " & INSTANCE_LABEL); + write(OUTPUT_LINE, NOW); + write(OUTPUT_LINE, (" " & INBOUND_LABEL & " Completion ID (CID=" & to_str(COMPLETION_ID) & ") is already in use: HEADER=0x")); + write_hex_vector(OUTPUT_LINE, HEADER); + write(OUTPUT_LINE, string'(", ADDRESS=0x")); + write_hex_vector(OUTPUT_LINE, HEADER_ADDR_HI); + write_hex_vector(OUTPUT_LINE, HEADER_ADDR_LOW); + writeline(OUT_FILE, OUTPUT_LINE); + else + + -- Make sure we have a budget for completion IDs + CID_COUNT := 0; + for I in 0 to (N_COMPLETION_ID-1) loop + if(INBOUND_READ_REQUEST_CPL_STATE(I) /= INBOUND_READ_REQUEST_ARRAY(I).STATE) then + CID_COUNT := CID_COUNT + 1; + end if; + end loop; + + if(CID_COUNT < N_INBOUND_RD_OUTSTANDING) then -- OK to accept the request + INBOUND_READ_REQUEST_ARRAY(COMPLETION_ID).ADDRESS <= HEADER_ADDR_HI & HEADER_ADDR_LOW; + INBOUND_READ_REQUEST_ARRAY(COMPLETION_ID).BAR_HIT <= to_mvl(BFM_BAR_HIT(1)) & to_mvl(BFM_BAR_HIT(0)); + INBOUND_READ_REQUEST_ARRAY(COMPLETION_ID).LENGTH <= to_int(HEADER_LENGTH); + INBOUND_READ_REQUEST_ARRAY(COMPLETION_ID).STATE <= not INBOUND_READ_REQUEST_ARRAY(COMPLETION_ID).STATE; + INBOUND_READ_REQUEST_ARRAY(COMPLETION_ID).TC <= HEADER_TC; + INBOUND_READ_REQUEST_ARRAY(COMPLETION_ID).V <= HEADER_V; + wait on INBOUND_READ_REQUEST_ARRAY; + else + write(OUTPUT_LINE, "-- ERROR: " & INSTANCE_LABEL); + write(OUTPUT_LINE, NOW); + write(OUTPUT_LINE, (" " & INBOUND_LABEL & " Number of outstanding read requests exceeded: HEADER=0x")); + write_hex_vector(OUTPUT_LINE, HEADER); + write(OUTPUT_LINE, string'(", ADDRESS=0x")); + write_hex_vector(OUTPUT_LINE, HEADER_ADDR_HI); + write_hex_vector(OUTPUT_LINE, HEADER_ADDR_LOW); + writeline(OUT_FILE, OUTPUT_LINE); + end if; + end if; + + elsif((HEADER_TYPE = "0100") or (HEADER_TYPE = "0101")) then -- Read Request + --*------------------------------------------------------------- + --* Process the Read Completion + --*------------------------------------------------------------- + + I_HEADER_LENGTH := to_int(HEADER_LENGTH); + I_CID := to_int(HEADER_CID); + + if(RD_BUFFER_PTR(I_CID) = 0) then -- start of a completion sequence + RD_CPL_RAM_ADDR(I_CID)(RAM_ADDR'high) := RD_BUFFER(I_CID).BAR_HIT(1); + RD_CPL_RAM_ADDR(I_CID)(RAM_ADDR'high-1 downto 0) := RD_BUFFER(I_CID).ADDRESS(RAM_ADDR'high-1 downto 2) & "00"; + end if; + +-- RAM_ADDR := RD_CPL_RAM_ADDR(I_CID)(RAM_ADDR'range); +-- CHANGE Can get rid of RD_CPL_RAM_ADDR + + loop + wait until(ICLK'event and (ICLK = '1') and (IN_VALID = '1')); + + I_HEADER_LENGTH := I_HEADER_LENGTH - 1; + +-- for J in 0 to 3 loop -- Do all 4 bytes of the read +-- +-- RAM_ADDR1 <= RAM_ADDR; +-- RAM_REQ1 <= not RAM_REQ1; +-- +-- wait on RAM_ACK1; +-- +-- CMP_DATA(J*8+7 downto J*8) := RAM_RD_DATA1; +-- RAM_ADDR := RAM_ADDR + '1'; +-- +-- end loop; + + + CMP_DATA := IN_DATA; + CMD_RD_DATA <= IN_DATA; + CMD_RD_DATA_VALID <= '1'; + wait for 1ns; + + CMD_RD_DATA_VALID <= '0'; + + + RD_DATA := RD_BUFFER(I_CID).DATA(RD_BUFFER_PTR(I_CID)); + RD_MASK := RD_BUFFER(I_CID).MASK(RD_BUFFER_PTR(I_CID)); + + read_cmp(OUTPUT_LINE, (INSTANCE_LABEL & "BFM Master Mode Read "), CMP_DATA, RD_DATA, RD_MASK, vERR); + if(vERR /= 0) then + TMP_I := RD_BUFFER_PTR(I_CID)*4; + TMP_I := to_int(RD_BUFFER(I_CID).ADDRESS(31 downto 0)) + TMP_I; + TMP32 := to_vector(TMP_I, TMP32'length); + write(OUTPUT_LINE, LF); + write(OUTPUT_LINE, string'("Header=0x")); + write_hex_vector(OUTPUT_LINE, HEADER); + write(OUTPUT_LINE, string'(", CID=0x")); + write_hex_vector(OUTPUT_LINE, HEADER_CID); + write(OUTPUT_LINE, string'(", ADDRESS=0x")); + write_hex_vector(OUTPUT_LINE, RD_BUFFER(I_CID).ADDRESS(63 downto 32)); + write_hex_vector(OUTPUT_LINE, RD_BUFFER(I_CID).DATA(0)); + + write_hex_vector(OUTPUT_LINE, TMP32); + writeline(OUT_FILE, OUTPUT_LINE); + end if; + + RD_BUFFER_PTR(I_CID) := RD_BUFFER_PTR(I_CID) + 1; + + if(IN_DFRAME = '0') or (I_HEADER_LENGTH = 0) then + exit; + end if; + + end loop; + + RD_CPL_RAM_ADDR(I_CID)(RAM_ADDR'range) := RAM_ADDR; + + + if not((IN_DFRAME = '0') and (I_HEADER_LENGTH = 0)) then + write(OUTPUT_LINE, "-- ERROR: " & INSTANCE_LABEL); + write(OUTPUT_LINE, NOW); + write(OUTPUT_LINE, (" " & INBOUND_LABEL & "_DFRAME is not in agreement with the header length value: HEADER=0x")); + write_hex_vector(OUTPUT_LINE, HEADER); + write(OUTPUT_LINE, string'(", ADDRESS=0x")); + write_hex_vector(OUTPUT_LINE, HEADER_ADDR_HI); + write_hex_vector(OUTPUT_LINE, HEADER_ADDR_LOW); + writeline(OUT_FILE, OUTPUT_LINE); + end if; + + if(HEADER_L = '1') then + OUTBOUND_READ_REQUEST_CPL_STATE(I_CID) <= RD_BUFFER(I_CID).STATE; + RD_BUFFER_PTR(I_CID) := 0; + end if; + + + + end if; + end if; + + + end loop; + end process; + + +--#########################################################################-- +-- +-- BFM Internal RAM Handler +-- +-- Handles SRAM access from multiple processes +-- +--#########################################################################-- + + process + --file OUT_FILE : text is out "STD_OUTPUT"; +-- file OUT_FILE : text open write_mode is "STD_OUTPUT"; +-- variable OUTPUT_LINE : line; + variable SRAM_BFM : MEM_ID_TYPE; + variable vRAM_DATA : std_ulogic_vector(7 downto 0); + + begin + + -- Initialize RAM model + SRAM_BFM := SRAM_INITIALIZE + (name => "BFM RAM", + length => (2**N_RAM_MAX), + width => 8, + default_word => std_ulogic_vector'("XXXXXXXX") + ); + + RAM_ACK0 <= false; + RAM_ACK1 <= false; + + loop + + wait on RAM_REQ0, RAM_REQ1; + + if(RAM_REQ0'event) then + if(RAM_WR0) then + Mem_Write + (mem_id => SRAM_BFM, + address => RAM_ADDR0, + data => RAM_WR_DATA0 + ); + wait for 1ns; + else + Mem_Read + (mem_id => SRAM_BFM, + address => RAM_ADDR0, + data => vRAM_DATA + ); + RAM_RD_DATA0 <= vRAM_DATA; + wait for 1ns; + end if; + + RAM_ACK0 <= not RAM_ACK0; +-- wait until(RAM_REQ0'event); + end if; + + if(RAM_REQ1'event) then + if(RAM_WR1) then + Mem_Write + (mem_id => SRAM_BFM, + address => RAM_ADDR1, + data => RAM_WR_DATA1 + ); + else + Mem_Read + (mem_id => SRAM_BFM, + address => RAM_ADDR1, + data => vRAM_DATA + ); + RAM_RD_DATA1 <= vRAM_DATA; + end if; + RAM_ACK1 <= not RAM_ACK1; +-- wait until(RAM_REQ1'event); + end if; + + end loop; + end process; + +--#########################################################################-- +-- +-- Random # generator +-- +-- Uses PRBS-23 +-- +--#########################################################################-- + + process(CLK0o) + variable RNDOUT : integer; + variable SEED : integer; + begin + if(RSTOUTo = '1') then + SEED := 5000; + elsif(CLK0o'event and CLK0o = '1') then + get_random(SEED, 0, 100, RNDOUT); + RANDOM_NUMBER <= RNDOUT; + end if; + end process; + +--#########################################################################-- +-- +-- L2P Bus Sniffer +-- +--#########################################################################-- + process + begin + wait until(L2P_CLKpi'event); + L2P_CLKi_90 <= transport L2P_CLKpi after (T_LCLKi/4); + end process; + + process + file OUT_FILE : text open write_mode is "NullFile"; + variable OUTPUT_LINE : line; + variable vHEADER : std_ulogic_vector(31 downto 0); + variable vADDRESS : std_ulogic_vector(63 downto 0); + variable vTYPE : std_ulogic_vector(3 downto 0); + variable START : time; + begin + wait until(L2P_CLKi_90'event and (L2P_CLKi_90 = '1') and (L2P_VALIDi = '1') and MODE_PRIMARY); + START := NOW; + if(L2P_DFRAMEi = '1') then + vHEADER(15 downto 0) := L2P_DATAi; + wait until(L2P_CLKi_90'event and (L2P_CLKi_90 = '0')); + vHEADER(31 downto 16) := L2P_DATAi; + vTYPE := vHEADER(27 downto 24); + vADDRESS := (others => '0'); + + -- Upper Address + if((vTYPE = "0001") or (vTYPE = "0011")) then -- address is 64 bits + wait until(L2P_CLKi_90'event and (L2P_CLKi_90 = '1') and (L2P_VALIDi = '1')); + vADDRESS(47 downto 32) := L2P_DATAi; + wait until(L2P_CLKi_90'event and (L2P_CLKi_90 = '0')); + vADDRESS(63 downto 48) := L2P_DATAi; + end if; + -- Lower Address + if((vTYPE = "0000") or (vTYPE = "0001") or (vTYPE = "0010") or (vTYPE = "0011") or (vTYPE = "0100")) then -- address is required + wait until(L2P_CLKi_90'event and (L2P_CLKi_90 = '1') and (L2P_VALIDi = '1')); + vADDRESS(15 downto 0) := L2P_DATAi; + wait until(L2P_CLKi_90'event and (L2P_CLKi_90 = '0')); + vADDRESS(31 downto 16) := L2P_DATAi; + end if; + +-- write(OUTPUT_LINE, ("-->> L2P Packet: " & to_string(START))); +-- writeline(OUT_FILE, OUTPUT_LINE); + write(OUTPUT_LINE, string'("-->>>> L2P Header: ")); + + case vTYPE is + when "0000" | "0001" => + write(OUTPUT_LINE, string'("(L2P Master Read Request)")); + write(OUTPUT_LINE, string'(", FBE=0x")); + write_hex_vector(OUTPUT_LINE, vHEADER(23 downto 20)); + write(OUTPUT_LINE, string'(", LBE=0x")); + write_hex_vector(OUTPUT_LINE, vHEADER(19 downto 16)); + write(OUTPUT_LINE, string'(", V=" & to_str(vHEADER(12)))); + write(OUTPUT_LINE, string'(", CID=")); + write_hex_vector(OUTPUT_LINE, vHEADER(11 downto 10)); + write(OUTPUT_LINE, string'(", LENGTH=0x")); + write_hex_vector(OUTPUT_LINE, vHEADER(9 downto 0)); +-- writeline(OUT_FILE, OUTPUT_LINE); + write(OUTPUT_LINE, string'("-->>>> Address: 0x")); + write_hex_vector(OUTPUT_LINE, vADDRESS); + write(OUTPUT_LINE, string'(" @ ")); + write(OUTPUT_LINE, START); + writeline(OUT_FILE, OUTPUT_LINE); + + when "0010" | "0011" => + write(OUTPUT_LINE, string'("(L2P Master Write)")); + write(OUTPUT_LINE, string'(", FBE=0x")); + write_hex_vector(OUTPUT_LINE, vHEADER(23 downto 20)); + write(OUTPUT_LINE, string'(", LBE=0x")); + write_hex_vector(OUTPUT_LINE, vHEADER(19 downto 16)); + write(OUTPUT_LINE, string'(", V=" & to_str(vHEADER(12)))); + write(OUTPUT_LINE, string'(", LENGTH=0x")); + write_hex_vector(OUTPUT_LINE, vHEADER(9 downto 0)); +-- writeline(OUT_FILE, OUTPUT_LINE); + write(OUTPUT_LINE, string'("-->>>> Address: 0x")); + write_hex_vector(OUTPUT_LINE, vADDRESS); + write(OUTPUT_LINE, string'(" @ ")); + write(OUTPUT_LINE, START); + writeline(OUT_FILE, OUTPUT_LINE); + + when "0100" => + write(OUTPUT_LINE, string'("(L2P Target Read Completion Without Data)")); + write(OUTPUT_LINE, string'(", STAT=")); + write_hex_vector(OUTPUT_LINE, vHEADER(17 downto 16)); + write(OUTPUT_LINE, string'(", L=" & to_str(vHEADER(15)))); + write(OUTPUT_LINE, string'(", V=" & to_str(vHEADER(12)))); + write(OUTPUT_LINE, string'(", CID=")); + write_hex_vector(OUTPUT_LINE, vHEADER(11 downto 10)); + write(OUTPUT_LINE, string'(", LENGTH=0x")); + write_hex_vector(OUTPUT_LINE, vHEADER(9 downto 0)); + write(OUTPUT_LINE, string'(" @ ")); + write(OUTPUT_LINE, START); + writeline(OUT_FILE, OUTPUT_LINE); + + when "0101" => + write(OUTPUT_LINE, string'("(L2P Target Read Completion With Data)")); + write(OUTPUT_LINE, string'(", STAT=")); + write_hex_vector(OUTPUT_LINE, vHEADER(17 downto 16)); + write(OUTPUT_LINE, string'(", L=" & to_str(vHEADER(15)))); + write(OUTPUT_LINE, string'(", V=" & to_str(vHEADER(12)))); + write(OUTPUT_LINE, string'(", CID=")); + write_hex_vector(OUTPUT_LINE, vHEADER(11 downto 10)); + write(OUTPUT_LINE, string'(", LENGTH=0x")); + write_hex_vector(OUTPUT_LINE, vHEADER(9 downto 0)); + write(OUTPUT_LINE, string'(" @ ")); + write(OUTPUT_LINE, START); + writeline(OUT_FILE, OUTPUT_LINE); + + when others => + write(OUTPUT_LINE, string'("(Undefined)")); + write(OUTPUT_LINE, string'(" @ ")); + write(OUTPUT_LINE, START); + writeline(OUT_FILE, OUTPUT_LINE); + assert false report "---- ERROR: Unsupported TYPE in L2P Header" + severity error; + end case; + + + + if(L2P_DFRAMEi = '1') then + wait until(L2P_CLKi_90'event and (L2P_CLKi_90 = '1') and (L2P_VALIDi = '1') and (L2P_DFRAMEi = '0')); + end if; + + else + write(OUTPUT_LINE, string'("-- ERROR: L2P Bus: P2L_VALID asserted without P2L_DFRAME @")); + write(OUTPUT_LINE, START); + writeline(OUT_FILE, OUTPUT_LINE); + end if; + end process; + +--#########################################################################-- +-- +-- P2L Bus Sniffer +-- +--#########################################################################-- + process + file OUT_FILE : text open write_mode is "NullFile"; + variable OUTPUT_LINE : line; + variable vHEADER : std_ulogic_vector(31 downto 0); + variable vADDRESS : std_ulogic_vector(63 downto 0); + variable vTYPE : std_ulogic_vector(3 downto 0); + variable START : time; + begin + wait until(P2L_CLKpi'event and (P2L_CLKpi = '1') and (P2L_VALIDi = '1') and MODE_PRIMARY); + START := NOW; + if(P2L_DFRAMEi = '1') then + vHEADER(15 downto 0) := P2L_DATAi; + wait until(P2L_CLKpi'event and (P2L_CLKpi = '0')); + vHEADER(31 downto 16) := P2L_DATAi; + vTYPE := vHEADER(27 downto 24); + vADDRESS := (others => '0'); + + -- Upper Address + if((vTYPE = "0001") or (vTYPE = "0011")) then -- address is 64 bits + wait until(P2L_CLKpi'event and (P2L_CLKpi = '1') and (P2L_VALIDi = '1')); + vADDRESS(47 downto 32) := P2L_DATAi; + wait until(P2L_CLKpi'event and (P2L_CLKpi = '0')); + vADDRESS(63 downto 48) := P2L_DATAi; + end if; + -- Lower Address + if((vTYPE = "0000") or (vTYPE = "0001") or (vTYPE = "0010") or (vTYPE = "0011") or (vTYPE = "0100")) then -- address is required + wait until(P2L_CLKpi'event and (P2L_CLKpi = '1') and (P2L_VALIDi = '1')); + vADDRESS(15 downto 0) := P2L_DATAi; + wait until(P2L_CLKpi'event and (P2L_CLKpi = '0')); + vADDRESS(31 downto 16) := P2L_DATAi; + end if; + + write(OUTPUT_LINE, string'("--<<<< P2L Header: ")); + + case vTYPE is + when "0000" | "0001" => + write(OUTPUT_LINE, string'("(P2L Target Read Request)")); + write(OUTPUT_LINE, string'(", FBE=0x")); + write_hex_vector(OUTPUT_LINE, vHEADER(23 downto 20)); + write(OUTPUT_LINE, string'(", LBE=0x")); + write_hex_vector(OUTPUT_LINE, vHEADER(19 downto 16)); + write(OUTPUT_LINE, string'(", V=" & to_str(vHEADER(12)))); + write(OUTPUT_LINE, string'(", CID=")); + write_hex_vector(OUTPUT_LINE, vHEADER(11 downto 10)); + write(OUTPUT_LINE, string'(", LENGTH=0x")); + write_hex_vector(OUTPUT_LINE, vHEADER(9 downto 0)); + write(OUTPUT_LINE, string'("--<<<< Address: 0x")); + write_hex_vector(OUTPUT_LINE, vADDRESS); + write(OUTPUT_LINE, string'(" @ ")); + write(OUTPUT_LINE, START); + writeline(OUT_FILE, OUTPUT_LINE); + + when "0010" | "0011" => + write(OUTPUT_LINE, string'("(P2L Target Write)")); + write(OUTPUT_LINE, string'(", FBE=0x")); + write_hex_vector(OUTPUT_LINE, vHEADER(23 downto 20)); + write(OUTPUT_LINE, string'(", LBE=0x")); + write_hex_vector(OUTPUT_LINE, vHEADER(19 downto 16)); + write(OUTPUT_LINE, string'(", V=" & to_str(vHEADER(12)))); + write(OUTPUT_LINE, string'(", LENGTH=0x")); + write_hex_vector(OUTPUT_LINE, vHEADER(9 downto 0)); + write(OUTPUT_LINE, string'("--<<<< Address: 0x")); + write_hex_vector(OUTPUT_LINE, vADDRESS); + write(OUTPUT_LINE, string'(" @ ")); + write(OUTPUT_LINE, START); + writeline(OUT_FILE, OUTPUT_LINE); + + when "0100" => + write(OUTPUT_LINE, string'("(P2L Master Read Completion Without Data)")); + write(OUTPUT_LINE, string'(", STAT=")); + write_hex_vector(OUTPUT_LINE, vHEADER(17 downto 16)); + write(OUTPUT_LINE, string'(", L=" & to_str(vHEADER(15)))); + write(OUTPUT_LINE, string'(", V=" & to_str(vHEADER(12)))); + write(OUTPUT_LINE, string'(", CID=")); + write_hex_vector(OUTPUT_LINE, vHEADER(11 downto 10)); + write(OUTPUT_LINE, string'(", LENGTH=0x")); + write_hex_vector(OUTPUT_LINE, vHEADER(9 downto 0)); + write(OUTPUT_LINE, string'(" @ ")); + write(OUTPUT_LINE, START); + writeline(OUT_FILE, OUTPUT_LINE); + + when "0101" => + write(OUTPUT_LINE, string'("(P2L Master Read Completion With Data)")); + write(OUTPUT_LINE, string'(", STAT=")); + write_hex_vector(OUTPUT_LINE, vHEADER(17 downto 16)); + write(OUTPUT_LINE, string'(", L=" & to_str(vHEADER(15)))); + write(OUTPUT_LINE, string'(", V=" & to_str(vHEADER(12)))); + write(OUTPUT_LINE, string'(", CID=")); + write_hex_vector(OUTPUT_LINE, vHEADER(11 downto 10)); + write(OUTPUT_LINE, string'(", LENGTH=0x")); + write_hex_vector(OUTPUT_LINE, vHEADER(9 downto 0)); + write(OUTPUT_LINE, string'(" @ ")); + write(OUTPUT_LINE, START); + writeline(OUT_FILE, OUTPUT_LINE); + + when others => + write(OUTPUT_LINE, string'("(Undefined)")); + write(OUTPUT_LINE, string'(" @ ")); + write(OUTPUT_LINE, START); + writeline(OUT_FILE, OUTPUT_LINE); + assert false report "---- ERROR: Unsupported TYPE in P2L Header" + severity error; + end case; + + + if(P2L_DFRAMEi = '1') then + wait until(P2L_CLKpi'event and (P2L_CLKpi = '1') and (P2L_VALIDi = '1') and (P2L_DFRAMEi = '0')); + end if; + + else + write(OUTPUT_LINE, string'("-- ERROR: P2L Bus: P2L_VALID asserted without P2L_DFRAME @")); + write(OUTPUT_LINE, START); + writeline(OUT_FILE, OUTPUT_LINE); + end if; + end process; + + +end MODEL; + + diff --git a/hdl/gn4124core/sim/gn4124_bfm/mem_model.vhd b/hdl/gn4124core/sim/gn4124_bfm/mem_model.vhd new file mode 100644 index 0000000000000000000000000000000000000000..f5d8f8a318efd4909ce49b73e48edbfd3bff3b25 --- /dev/null +++ b/hdl/gn4124core/sim/gn4124_bfm/mem_model.vhd @@ -0,0 +1,2324 @@ +library ieee; +use ieee.std_logic_1164.all; +library std; +use std.textio.all; + +PACKAGE mem_model is + + -- 2d array to store data for each row + type row_matrix is array (NATURAL RANGE <>, NATURAL RANGE <>) of UX01; + type rowptr_type is access row_matrix; + -- record for for storing refresh and memory ptr for each row + type row_data_type is + record + last_refresh : time; -- last time row was refreshed + rowptr : rowptr_type; -- ptr to 2d matrix with data + all_xs : BOOLEAN; -- true if row is filled with Xs + end record; + -- array of refresh times and memory ptrs for the rows + type row_data is array (NATURAL RANGE <>) of row_data_type; + type row_data_ptr_type is access row_data; + type strptr is access string; + type default_ptr_type is access std_logic_vector; + type mem_type is (DRAM, SRAM, ROM); -- memory types + + -- record defining memory and holding general information + + type mem_id_rtype is + + record + memory_type : mem_type; -- memory type + refresh_period : time; -- refresh period + last_init : time; -- last time a refresh was performed + counter : NATURAL; -- refresh counter + name : strptr; -- pointer to memory name + rows : POSITIVE; -- # of rows + columns : POSITIVE; -- # of columns + width : POSITIVE; -- # word length + length : POSITIVE; -- # of memory locations + row_data_ptr : row_data_ptr_type; -- ptr to memory ptrs. + default : default_ptr_type; -- ptr to default memory word value + end record; + + type mem_id_type is access mem_id_rtype; + + +--******************************************************************************** + -- Function Name : SRAM_Initialize + -- + -- Purpose : To create the data structure used to store a + -- static RAM and to initialize it + -- + -- Parameters : name - string used to represent the memory + -- length - the number of "words" in the memory + -- width - the length of a "word" of memory + -- default_word - value to which each word of memory + -- should be initialized + -- + -- RETURNED VALUE : mem_id_type - ptr to memory record + -- + -- NOTE : initially the data structure is empty with no + -- space being allocated for the memory + -- + -- Use : sram_l1 := SRAM_Initialize ("lsb_of_RAM",1048576,1,"0"); +--******************************************************************************** + + impure Function SRAM_Initialize ( Constant name : IN string; + Constant length : IN POSITIVE; + Constant width : IN POSITIVE; + Constant default_word : IN std_ulogic_vector + ) return mem_id_type; + +--******************************************************************************** + -- Procedure Name : Mem_Read + -- + -- Purpose : To read a "word" from memory + -- + -- Parameters : mem_id - ptr to memory data structure + -- address - address to read from + -- data - contents of memory location + -- + -- + -- NOTE : a read refreshes row of a DRAM + -- + -- Use : Mem_Read (ROM1, "100100111", data_bus); +--******************************************************************************** + + Procedure Mem_Read ( Variable mem_id : INOUT mem_id_type; + Constant address : IN std_ulogic_vector; + Variable data : OUT std_ulogic_vector + ); + +--******************************************************************************** + -- Procedure Name : Mem_Write + -- + -- Purpose : To write a "word" to memory + -- + -- Parameters : mem_id - ptr to memory data structure + -- address - address to read from + -- data - "word" to be written to memory + -- + -- NOTE : a write refreshes row of a DRAM + -- + -- Use : Mem_Write (ROM1, "100100111", "10X1"); +--******************************************************************************** + + Procedure Mem_Write ( Variable mem_id : INOUT mem_id_type; + Constant address : IN std_ulogic_vector; + Constant data : IN std_ulogic_vector + ); + +END mem_model; + + + + + + +PACKAGE BODY mem_model is + + Type D1_b_ulogic_type is array(bit) of std_ulogic; + type hex_ray is array(1 to 16) of character; + type IDENTIFIER is (HEX_NUM1, COMMENT1, WIDTH1, DEFAULT1, COLON1, DOTDOT1, BLANK1, SYN_ERROR1); + type digit_to_hex_type is array(0 to 15) of character; + + -- mentor doesn't like the subtype UX01 - "resolved sybyte cannot be used as a discrete range" + type UX01_1DRAY is array(std_ulogic range 'U' to '1') of bit; + + ------------------------------------------------------------------------------------------- + -- THE FOLLOWING CONSTANTS MAY BE CHANGED BY THE USER TO CUSTOMIZE STD_MEMPAK + ------------------------------------------------------------------------------------------- + + -- defines the number of bits used to represent an integer on the machine used to run the vhdl simulator + CONSTANT IntegerBitLength : INTEGER := 32; + + -- defines the maximum length of strings in this package + + CONSTANT MAX_STR_LEN : NATURAL := 256; + + -- constants used to map X's and U's in an address to valid values + + CONSTANT ADDRESS_X_MAP : BIT := '1'; + CONSTANT ADDRESS_U_MAP : BIT := '1'; + CONSTANT ADDRESS_MAP : UX01_1DRAY := + (ADDRESS_U_MAP, ADDRESS_X_MAP, '0', '1'); + + -- constants used to map X's and U's in memory locations to a bit value + -- when a bit or a bit_vector is returned by the memory read operation + + CONSTANT DATA_X_MAP : BIT := '1'; + CONSTANT DATA_U_MAP : BIT := '1'; + CONSTANT DATA_MAP : UX01_1DRAY := + (DATA_U_MAP, DATA_X_MAP, '0', '1'); + + -- constants setting collumn size of SRAM's and ROM's so that entire + -- memory does not have to be allocated if it is not used. + + CONSTANT SRAM_COL_SIZE : NATURAL := 1024; + CONSTANT ROM_COL_SIZE : NATURAL := 1024; + + -- constant used to enable/disable certain warning assertions + CONSTANT MEM_WARNINGS_ON : BOOLEAN := TRUE; + + -- constant used to determine how many words per line to output when doing a memory dump + CONSTANT WORDS_PER_LINE : POSITIVE := 16; + + + ---------------------------------------------------------------------------------------------------------- + -- CONSTANTS THAT SHOULD NOT BE MODIFIED + -- These are used by the package to perform various conversions, comparisions, etc. + ---------------------------------------------------------------------------------------------------------- + + CONSTANT DIGIT_TO_HEX : digit_to_hex_type := ('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'); + CONSTANT hex : hex_ray := ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'); + CONSTANT bit_to_std_ulogic : D1_b_ulogic_type := ('0', '1'); + CONSTANT SPACESTR : STRING(1 to 20) := " "; + CONSTANT SPACE : CHARACTER := ' '; + CONSTANT TAB : CHARACTER := HT; + + + + +function StrLen1 ( Constant l_str : IN string ) return NATURAL is + +Variable alias_l_str : string(1 to l_str'length) := l_str; +Variable i : integer := 1; + +begin + while ( (i <= l_str'length) and (alias_l_str(i) /= NUL) ) loop + i := i + 1; + end loop; + i := i - 1; + return i; +end; + +function to_str (Constant dd : IN std_logic) return Character is + +begin + case dd is + when '1' => return '1'; + when '0' => return '0'; + when 'U' => return 'U'; + when 'X' => return 'X'; + when 'L' => return 'L'; + when 'H' => return 'H'; + when '-' => return '-'; + when 'Z' => return 'Z'; + when 'W' => return 'W'; + end case; +end; + +function to_str (Constant dd : IN bit) return character is + +begin + if dd = '0' then + return '0'; + else + return '1'; + end if; +end; + +function i_to_str (Constant int : IN integer) return string is + +Constant length : integer := 33; +Variable i, len, pos : integer; +Variable str : string (1 to length); +Variable tint : integer := int; +Variable temp : Character; +Variable negative : BOOLEAN := FALSE; + +begin + for i in 1 to length loop + str(i) := ' '; + end loop; + if (tint < 0 ) then + tint := -tint; + negative := TRUE; + end if; + i := length; + while ( (i >= 1 ) and (tint /= 0)) loop + str(i) := CHARACTER'Val(48 + (tint mod 10)); + tint := tint/10; + i := i - 1; + end loop; + if (NEGATIVE) then + str(i) := '-'; + i := i - 1; + end if; + len := length - i; + pos := i + 1; + for i in 1 to len loop + str(i) := str(pos); + pos := pos + 1; + end loop; + if (len = 0) then + len := 1; + str(1) := '0'; + end if; + return (str(1 to len)); + +end; + +function v_to_str (Constant vect : IN bit_vector) return string is + +Variable str : string( 1 to vect'length); +Variable alias_vect : bit_vector(1 to vect'length) := vect; +Variable i : integer; + +begin + for i in 1 to vect'length loop + case alias_vect(i) is + when '1' => str(i) := '1'; + when '0' => str(i) := '0'; + end case; + end loop; + return(str); +end; + + +function v_to_str (Constant vect : IN std_logic_vector) return string is + +Variable str : string( 1 to vect'length); +Variable alias_vect : std_logic_vector(1 to vect'length) := vect; +Variable i : integer; + +begin + for i in 1 to vect'length loop + case alias_vect(i) is + when '1' => str(i) := '1'; + when '0' => str(i) := '0'; + when 'U' => str(i) := 'U'; + when 'X' => str(i) := 'X'; + when 'L' => str(i) := 'L'; + when 'H' => str(i) := 'H'; + when '-' => str(i) := '-'; + when 'Z' => str(i) := 'Z'; + when 'W' => str(i) := 'W'; + end case; + end loop; + return(str); +end; + +-- used to return a printable string for the memory name +function pstr ( Constant name : in string ) return string is + + variable j : integer; + +begin + j := 1; + while ( (j < name'length) and (name(j) /= nul) ) loop + j := j + 1; + end loop; + if (name(j) = nul) then + j := j - 1; + end if; + return name(1 to j); +end; + + + + --------------------------------------------------------------------------- + -- Function Name : minimum + -- + -- PURPOSE : to determine the smaller of two integers + -- + -- Parameters : int1 - first integer + -- : int2 - second integer + -- + -- Returned Value : integer - the smaller of int1 and int2 + -- + --------------------------------------------------------------------------- + + Function minimum ( Constant int1 : IN integer; + Constant int2 : IN integer + ) return integer is + + begin + if (int1 < int2) then + return int1; + else + return int2; + end if; + end; + +--+----------------------------------------------------------------------------- +--| Procedure Name : StrCpy1 +--| 1.2.3 +--| Overloading : None +--| +--| Purpose : Copy r_string to l_string. +--| +--| Parameters : +--| l_str - output, STRING, target string +--| r_str - input, STRING, source string +--| +--| Result : +--| +--| NOTE : If the length of target string is greater than +--| the source string, then target string is padded +--| with space characters on the right side and when +--| the length of target string is shorter than the +--| length of source string only left most characters +--| of the source string will be be copied to the target. +--| +--| +--| USE : +--| Variable s1: string(1 TO 8); +--| +--| StrCpy1(s1, "123456789A"); +--| s1 will hold "12345678" +--|----------------------------------------------------------------------------- + PROCEDURE StrCpy1 ( VARIABLE l_str : OUT STRING; + CONSTANT r_str : IN STRING) IS + VARIABLE l_len : integer := l_str'LENGTH; + VARIABLE r_len : integer := r_str'LENGTH; + VARIABLE r : STRING ( 1 to r_len) := r_str; + VARIABLE result : STRING (1 to l_len); + VARIABLE indx : integer := 1; + BEGIN + assert (l_len > 0) + report "StrCpy: target string is of zero length " + severity ERROR; + + while ( (indx <= r_len) and (indx <= l_len) and (r(indx) /= NUL) ) loop + result(indx) := r(indx); + indx := indx + 1; + end loop; + if (indx <= l_len) then + result(indx) := NUL; + end if; + l_str := result; + return; + END StrCpy1; + + + +--+--------------------------------------------------------------------------- +--| Procedure Name : fgetline1 +--| +--| Overloading : None +--| +--| Purpose : To read a line from the input TEXT file and +--| save into a string. +--| +--| Parameters : +--| l_str -- output, STRING, +--| stream -- input, TEXT, input file +--| +--| result : string. +--| +--| Note: : The TEXT is defined in the package TEXTIO to be +--| a file of string. +--| USE: : +--| VARIABLE line_buf : string(1 TO 256); +--| FILE in_file : TEXT IS IN "file_text_in.dat"; +--| +--| fgetline1(line_buf, in_file); +--| +--| Will read a line from the file +--| file_text_in.dat and place into line_buf. +--| +--|----------------------------------------------------------------------------- + PROCEDURE fgetline1 ( VARIABLE l_str : OUT STRING; + VARIABLE stream : IN TEXT; + VARIABLE line_ptr : INOUT LINE + ) IS + VARIABLE str_copy : STRING(1 TO MAX_STR_LEN + 1); + VARIABLE ch : character; + VARIABLE indx : NATURAL := 0; + BEGIN + If ( (line_ptr /= NULL) and (line_ptr'LENGTH > 0) ) then + NULL; + elsif ( not ENDFILE(stream) ) then + READLINE(stream, line_ptr); + else + assert NOT MEM_WARNINGS_ON + report " fgetline1 --- end of file text, no text read " + severity WARNING; + l_str(l_str'left) := NUL; + return; + end if; + while ( (line_ptr /= NULL) and (line_ptr'length /= 0) ) loop + READ(line_ptr,ch); + indx := indx + 1; + str_copy(indx) := ch; + end loop; + str_copy(indx + 1) := NUL; + strcpy1(l_str, str_copy); + return; + END; + + + +--+----------------------------------------------------------------------------- +--| Function Name : Is_White1 +--| hidden. +--| Overloading : None +--| +--| Purpose : Test whether a character is a blank, a tab or +--| a newline character. +--| +--| Parameters : +--| c - input Character. +--| +--| Result :Booelan -- True if the argument c is a blank or a tab(HT), +--| or a line feed (LF), or carriage return (CR). false otherwise. +--| +--| +--| See Also : Is_Space +--|----------------------------------------------------------------------------- + FUNCTION Is_White1 ( CONSTANT c : IN CHARACTER + ) RETURN BOOLEAN IS + VARIABLE result : BOOLEAN; + BEGIN + IF ( (c = ' ') OR (c = HT) OR (c = CR) OR (c=LF) ) THEN + result := TRUE; + ELSE + result := FALSE; + END IF; + RETURN result; + + END; + +--+----------------------------------------------------------------------------- +--| Function Name : Find_NonBlank1 +--| hidden +--| Overloading : None +--| +--| Purpose : Find first non_blank character in a string. +--| +--| Parameters : +--| str_in - input , +--| +--| Result : Natural, index of non_blank character. If string +--| has all the white character then str_in'LENGTH is +--| returned; +--| +--| NOTE : +--| +--| Use : +--| VARIABLE s_flag : String(1 TO 10) := " TRUE"; +--| VARIABLE idx: Natural +--| +--| idx := Find_NonBlank1 (s_flag); +--| +--|----------------------------------------------------------------------------- + FUNCTION Find_NonBlank1 ( CONSTANT str_in : IN STRING + ) RETURN NATURAL IS + VARIABLE str_copy : STRING (1 TO str_in'LENGTH) := str_in; + VARIABLE index : Natural := 1; + VARIABLE ch : character; + + BEGIN + loop + EXIT WHEN (index > str_in'LENGTH); + if Is_White1(str_copy(index)) then + index := index + 1; + else + EXIT; + end if; + end loop; + return index; +-- +-- old code +-- +-- ch := str_copy(index); +-- while ( ( index < str_in'LENGTH) AND (Is_White1(ch) ) ) LOOP +-- index := index + 1; +-- ch := str_copy(index); +-- end LOOP; +-- return index; + END; + +--+----------------------------------------------------------------------------- +--| Function Name : To_Upper1 +--| 1. +--| Overloading : None +--| +--| Purpose :Convert a string to upper case. +--| +--| Parameters : +--| val - input, string to be converted +--| +--| Result : string . +--| +--| +--| See Also : To_Lower, Is_Upper, Is_Lower +--|----------------------------------------------------------------------------- + FUNCTION To_Upper1 ( CONSTANT val : IN String + ) RETURN STRING IS + VARIABLE result : string (1 TO val'LENGTH) := val; + VARIABLE ch : character; + BEGIN + FOR i IN 1 TO val'LENGTH LOOP + ch := result(i); + EXIT WHEN ((ch = NUL) OR (ch = nul)); + IF ( ch >= 'a' and ch <= 'z') THEN + result(i) := CHARACTER'VAL( CHARACTER'POS(ch) + - CHARACTER'POS('a') + + CHARACTER'POS('A') ); + END IF; + END LOOP; + RETURN result; + END To_Upper1; + +--+----------------------------------------------------------------------------- +--| Function Name : From_HexString1 +--| +--| Overloading : None +--| +--| Purpose : Convert from a Hex String to a bit_vector. +--| +--| Parameters : +--| str - input , Hex string to be converted, +--| +--| Result : bit_vector +--| +--| NOTE : +--| +--| Use : +--| VARIABLE b_vect : bit_vector( 15 DOWNTO 4) ; +--| +--| b_vect := From_HexString1 (" 3DD 1010"); +--| This statement will set b_vect equal to "001111011101". +--| +--|----------------------------------------------------------------------------- + FUNCTION From_HexString1 ( CONSTANT str : IN STRING + ) RETURN bit_vector IS + + CONSTANT len : Integer := 4 * str'LENGTH; + CONSTANT hex_dig_len : Integer := 4; + VARIABLE str_copy : STRING (1 TO str'LENGTH) := To_Upper1(str); + VARIABLE index : Natural; + VARIABLE ch : character; + VARIABLE i, idx : Integer; + VARIABLE invalid : boolean := false; + VARIABLE r : bit_vector(1 TO len) ; + VARIABLE result : bit_vector(len - 1 DOWNTO 0) ; + CONSTANT BIT_ZERO : bit_vector(1 to 4) := "0000"; + CONSTANT BIT_ONE : bit_vector(1 to 4) := "0001"; + CONSTANT BIT_TWO : bit_vector(1 to 4) := "0010"; + CONSTANT BIT_THREE : bit_vector(1 to 4) := "0011"; + CONSTANT BIT_FOUR : bit_vector(1 to 4) := "0100"; + CONSTANT BIT_FIVE : bit_vector(1 to 4) := "0101"; + CONSTANT BIT_SIX : bit_vector(1 to 4) := "0110"; + CONSTANT BIT_SEVEN : bit_vector(1 to 4) := "0111"; + CONSTANT BIT_EIGHT : bit_vector(1 to 4) := "1000"; + CONSTANT BIT_NINE : bit_vector(1 to 4) := "1001"; + CONSTANT BIT_TEN : bit_vector(1 to 4) := "1010"; + CONSTANT BIT_ELEVEN : bit_vector(1 to 4) := "1011"; + CONSTANT BIT_TWELVE : bit_vector(1 to 4) := "1100"; + CONSTANT BIT_THIRTEEN: bit_vector(1 to 4) := "1101"; + CONSTANT BIT_FOURTEEN: bit_vector(1 to 4) := "1110"; + CONSTANT BIT_FIFTEEN : bit_vector(1 to 4) := "1111"; + + BEGIN + -- Check for null input + IF (str'LENGTH = 0) THEN + assert false + report " From_HexString1 --- input string has zero length "; + RETURN ""; + + ELSIF (str(str'LEFT) = NUL) THEN + assert false + report " From_HexString1 --- input string has nul character" + & " at the LEFT position " + severity ERROR; + RETURN ""; -- null bit_vector + END IF; + -- find the position of the first non_white character + index := Find_NonBlank1(str_copy); + IF (index > str'length) THEN + assert false + report " From_HexString1 --- input string is empty "; + RETURN ""; + ELSIF (str_copy(index)=NUL) THEN + assert false report " From_HexString1 -- first non_white character is a NUL "; + RETURN ""; + END IF; + + i := 0; + FOR idx IN index TO str'length LOOP + ch := str_copy(idx); + EXIT WHEN ((Is_White1(ch)) OR (ch = NUL)); + CASE ch IS + WHEN '0' => r(i+1 TO i+ hex_dig_len) := BIT_ZERO; + WHEN '1' => r(i+1 TO i+ hex_dig_len) := BIT_ONE; + WHEN '2' => r(i+1 TO i+ hex_dig_len) := BIT_TWO; + WHEN '3' => r(i+1 TO i+ hex_dig_len) := BIT_THREE; + WHEN '4' => r(i+1 TO i+ hex_dig_len) := BIT_FOUR; + WHEN '5' => r(i+1 TO i+ hex_dig_len) := BIT_FIVE; + WHEN '6' => r(i+1 TO i+ hex_dig_len) := BIT_SIX; + WHEN '7' => r(i+1 TO i+ hex_dig_len) := BIT_SEVEN; + WHEN '8' => r(i+1 TO i+ hex_dig_len) := BIT_EIGHT; + WHEN '9' => r(i+1 TO i+ hex_dig_len) := BIT_NINE; + WHEN 'A' | 'a' => r(i+1 TO i+ hex_dig_len) := BIT_TEN; + WHEN 'B' | 'b' => r(i+1 TO i+ hex_dig_len) := BIT_ELEVEN; + WHEN 'C' | 'c' => r(i+1 TO i+ hex_dig_len) := BIT_TWELVE; + WHEN 'D' | 'd' => r(i+1 TO i+ hex_dig_len) := BIT_THIRTEEN; + WHEN 'E' | 'e' => r(i+1 TO i+ hex_dig_len) := BIT_FOURTEEN; + WHEN 'F' | 'f' => r(i+1 TO i+ hex_dig_len) := BIT_FIFTEEN; + WHEN NUL => exit; + WHEN OTHERS => -- a non binary value was passed + invalid := TRUE; + ASSERT FALSE + REPORT "From_HexString1(str(" & i_to_str(idx) & ") => " + & ch & ") is an invalid character" + SEVERITY ERROR; + END CASE; + i := i + hex_dig_len; + END LOOP; + -- check for invalid character in the string + if ( invalid ) THEN + r(1 TO i) := (OTHERS => '0'); + end if; + result(i - 1 DOWNTO 0) := r(1 TO i); + return result(i - 1 DOWNTO 0); -- return slice of result + + END; + + ------------------------------------------------------------------------------- + -- Function Name : RegFill1 + -- 1.7.4 + -- Overloading : None + -- + -- Purpose : Fill an std_logic_vector with a given value + -- + -- Parameters : + -- SrcReg - input std_logic_vector, the logic vector to be read. + -- DstLength - input NATURAL, length of the return logic vector. + -- FillVal - input std_ulogic, default is '0' + -- + -- Result : std_logic_vector of length DstLength + -- + -- NOTE : The length of the return logic vector is specified by the + -- parameter 'DstLength'. The input logic vector will + -- be filled with the FillVal + -- + -- Use : + -- VARIABLE vect : std_logic_vector ( 15 DOWNTO 0 ); + -- vect := RegFill1 ( "00000101", 16, 'U'); + -- + -- See Also : SignExtend + ------------------------------------------------------------------------------- + FUNCTION RegFill1 ( CONSTANT SrcReg : IN std_logic_vector; + CONSTANT DstLength : IN NATURAL; + CONSTANT FillVal : IN std_ulogic := '0' + ) RETURN std_logic_vector IS + CONSTANT reslen : INTEGER := DstLength; + VARIABLE result : std_logic_vector (reslen - 1 DOWNTO 0) := (OTHERS => '0'); + VARIABLE reg : std_logic_vector (SrcReg'LENGTH - 1 DOWNTO 0) := SrcReg; + BEGIN + -- null range check + IF (SrcReg'LENGTH = 0) THEN + IF (DstLength = 0) THEN + ASSERT FALSE + REPORT " RegFill1 --- input has null range and" & + " Destination also has null range. " + SEVERITY ERROR; + RETURN result ; + ELSE + ASSERT FALSE + REPORT " RegFill1 --- input has null range" + SEVERITY ERROR; + result := (OTHERS => FillVal); + RETURN result ; + END IF; + + ELSIF (DstLength = 0) THEN + ASSERT false + REPORT "RegFill1 --- Destination has null range " + SEVERITY ERROR; + RETURN result; + + ELSIF (DstLength <= SrcReg'LENGTH) THEN + -- no need to sign extend + ASSERT (DstLength = SrcReg'LENGTH) + REPORT " RegFill1 --- Destination length is less than source" + SEVERITY ERROR; + RETURN reg; -- return the input data without any change + + ELSE + result(SrcReg'LENGTH - 1 DOWNTO 0) := reg; + -- Fill the MSB's of result with the given fill value. + For i IN reslen - 1 DOWNTO SrcReg'LENGTH Loop + result(i) := FillVal; + END LOOP; + END IF; + + -- convert to X01 + result := To_X01(result); + -- That's all + RETURN result; + END; + + + + --+----------------------------------------------------------------------------- + --| Function Name : bv_To_StdLogicVector + --| + --| Overloading : + --| + --| Purpose : Translate a BIt_VECTOR into an std_logic_vector. + --| + --| Parameters : SrcVect - input bit_vector , the value to be + --| translated. + --| width - input NATURAL, length of the return vector. + --| Default is IntegerBitLength (Machine integer length). + --| + --| Result : Std_logic_vector. + --| + --| NOTE : ****** this function not visible to the user ********** + --|----------------------------------------------------------------------------- + + -- **** function modified so as not to produce an assertion for a zero length vector + + FUNCTION bv_To_StdLogicVector ( CONSTANT SrcVect : IN Bit_Vector; + CONSTANT width : IN Natural := 0 + ) RETURN Std_Logic_Vector IS + + VARIABLE len : INTEGER := SrcVect'LENGTH; + VARIABLE result : Std_Logic_Vector(width - 1 DOWNTO 0) := (OTHERS=>'0'); + VARIABLE loc_res : Std_Logic_Vector(len - 1 DOWNTO 0) := (OTHERS =>'0'); + VARIABLE vect_copy : Bit_Vector(len - 1 DOWNTO 0) := SrcVect; + + BEGIN + + IF (SrcVect'LENGTH = 0) THEN + + return loc_res; + + ELSE + FOR i IN 0 TO len - 1 LOOP + CASE vect_copy(i) IS + WHEN '0' => + loc_res(i) := '0'; + WHEN '1' => + loc_res(i) := '1'; + END CASE; + END LOOP; + + IF (width = 0) THEN + return loc_res; + ELSIF (width <= SrcVect'LENGTH) THEN + result := loc_res(width - 1 DOWNTO 0); + ELSIF (width > SrcVect'LENGTH) THEN + result := RegFill1(loc_res, width, '0'); + END IF; + RETURN result; + + END IF; + + END; + + + FUNCTION bv_to_hexstr ( CONSTANT val : IN BIT_VECTOR + ) RETURN STRING IS + + CONSTANT hex_len : integer := (val'LENGTH + 3) / 4; + VARIABLE bin_str : STRING(1 to val'LENGTH); + VARIABLE hex_str : STRING(1 to hex_len); + VARIABLE hex_char : STRING(1 to 4); + VARIABLE bit_index : integer; + VARIABLE extended_bin_str : STRING(1 to hex_len * 4) := (others => '0'); + + + BEGIN + bin_str := v_to_str (val); + if ( (val'LENGTH mod 4) /= 0 ) then + extended_bin_str ( 5 - (val'LENGTH mod 4) to hex_len * 4 ) := bin_str; + else + extended_bin_str := bin_str; + end if; + FOR i IN 1 TO hex_len LOOP + bit_index := ((i - 1) * 4) + 1; + hex_char := extended_bin_str(bit_index To bit_index + 3); + CASE hex_char IS + WHEN "0000" => hex_str(i) := '0'; + WHEN "0001" => hex_str(i) := '1'; + WHEN "0010" => hex_str(i) := '2'; + WHEN "0011" => hex_str(i) := '3'; + WHEN "0100" => hex_str(i) := '4'; + WHEN "0101" => hex_str(i) := '5'; + WHEN "0110" => hex_str(i) := '6'; + WHEN "0111" => hex_str(i) := '7'; + WHEN "1000" => hex_str(i) := '8'; + WHEN "1001" => hex_str(i) := '9'; + WHEN "1010" => hex_str(i) := 'A'; + WHEN "1011" => hex_str(i) := 'B'; + WHEN "1100" => hex_str(i) := 'C'; + WHEN "1101" => hex_str(i) := 'D'; + WHEN "1110" => hex_str(i) := 'E'; + WHEN "1111" => hex_str(i) := 'F'; + WHEN OTHERS => null; + END CASE; + END LOOP; + return (hex_str); + + END; + + --------------------------------------------------------------------------- + -- Function Name : vector_size + -- + -- PURPOSE : to determine the maximum number of bits needed to + -- represent an integer + -- + -- Parameters : int - integer whose bit width is determined + -- + -- Returned Value : NATURAL - # of bits needed + -- + --------------------------------------------------------------------------- + + function vector_size ( Constant int : IN integer ) return natural is + + variable i : integer := int; + variable size : integer := 0; + + begin + while i > 0 loop + i := i / 2; + size := size + 1; + end loop; + return size; + end; + + --------------------------------------------------------------------------- + -- Function Name : address_trans + -- + -- Purpose : to translate an address in vector form to a + -- NATURAL + -- + -- Parameters : addr - address to be translated + -- + -- Returned Value : NATURAL - address as a natural number + -- + -- NOTE : ***** this procedure is NOT user visible ******* + -- + -- Use : address_trans(addr) + --------------------------------------------------------------------------- + + Function address_trans ( Constant mem_length : IN POSITIVE; + Constant addr : IN std_logic_vector + ) return NATURAL is + + Variable nad, power : NATURAL; + Variable uonce : BOOLEAN := TRUE; + Variable xonce : BOOLEAN := TRUE; + Variable vect_size : integer := vector_size(mem_length - 1); + Variable talias_addr : std_logic_vector(addr'length - 1 downto 0) := To_UX01(addr); + Variable alias_addr : std_logic_vector(vect_size - 1 downto 0) := (others => To_StdULogic(ADDRESS_X_MAP)); + Variable temp_vect : bit_vector(vect_size - 1 downto 0); + + begin + nad := 0; + power := 1; + alias_addr( minimum(vect_size, addr'length) - 1 downto 0) := + talias_addr( minimum(vect_size,addr'length) - 1 downto 0 ); + assert ( (vect_size >= addr'length) or NOT MEM_WARNINGS_ON ) + report "Bit width of address vector greater than that needed to access the entire memory." + & LF & SPACESTR & "passed address bit width: " & i_to_str(addr'length) + & LF & SPACESTR & "required address bit width: " & i_to_str(vect_size) + severity WARNING; + assert ( (vect_size <= addr'length) or NOT MEM_WARNINGS_ON ) + report "Bit width of address vector less than that needed to access the entire memory." + & LF & SPACESTR & "Resulting X's being mapped to: " & to_str(ADDRESS_X_MAP) + & LF & SPACESTR & "passed address bit width: " & i_to_str(addr'length) + & LF & SPACESTR & "required address bit width: " & i_to_str(vect_size) + severity WARNING; + + for i IN 0 to vect_size - 1 loop + if ((alias_addr(i) = 'U') and MEM_WARNINGS_ON and uonce) then + uonce := FALSE; + assert FALSE + report "Address contains a U - it is being mapped to: " & to_str(ADDRESS_U_MAP) + severity WARNING; + end if; + if ((alias_addr(i) = 'X') and MEM_WARNINGS_ON and xonce) then + xonce := FALSE; + assert false + report "Address contains an X - it is being mapped to: " & to_str(ADDRESS_X_MAP) + severity WARNING; + end if; + temp_vect(i) := ADDRESS_MAP(alias_addr(i)); + nad := nad + (power * bit'pos(temp_vect(i))); + power := power * 2; + end loop; + return nad; + end; + + Function address_trans ( Constant mem_length : IN POSITIVE; + Constant addr : IN std_ulogic_vector + ) return NATURAL is + + Variable nad, power : NATURAL; + Variable uonce : BOOLEAN := TRUE; + Variable xonce : BOOLEAN := TRUE; + Variable talias_addr : std_ulogic_vector(addr'length - 1 downto 0) := To_UX01(addr); + Variable vect_size : integer := vector_size(mem_length - 1); + Variable alias_addr : std_ulogic_vector(vect_size - 1 downto 0) := (others => To_StdULogic(ADDRESS_X_MAP)); + Variable temp_vect : bit_vector(vect_size - 1 downto 0); + + begin + nad := 0; + power := 1; + alias_addr( minimum(vect_size, addr'length) - 1 downto 0) := + talias_addr( minimum(vect_size,addr'length) - 1 downto 0); + assert ( (vect_size >= addr'length) or NOT MEM_WARNINGS_ON ) + report "Bit width of address vector greater than that needed to access the entire memory." + & LF & SPACESTR & "passed address bit width: " & i_to_str(addr'length) + & LF & SPACESTR & "required address bit width: " & i_to_str(vect_size) + severity WARNING; + assert ( (vect_size <= addr'length) or NOT MEM_WARNINGS_ON ) + report "Bit width of address vector less than that needed to access the entire memory." + & LF & SPACESTR & "Resulting X's being mapped to: " & to_str(ADDRESS_X_MAP) + & LF & SPACESTR & "passed address bit width: " & i_to_str(addr'length) + & LF & SPACESTR & "required address bit width: " & i_to_str(vect_size) + severity WARNING; + + for i IN 0 to vect_size - 1 loop + if ((alias_addr(i) = 'U') and MEM_WARNINGS_ON and uonce) then + uonce := FALSE; + assert false + report "Address contains a U - it is being mapped to: " & to_str(ADDRESS_U_MAP) + severity WARNING; + end if; + if ((alias_addr(i) = 'X') and MEM_WARNINGS_ON and xonce) then + xonce := FALSE; + assert false + report "Address contains an X - it is being mapped to: " & to_str(ADDRESS_X_MAP) + severity WARNING; + end if; + temp_vect(i) := ADDRESS_MAP(alias_addr(i)); + nad := nad + (power * bit'pos(temp_vect(i))); + power := power * 2; + end loop; + return nad; + end; + + Function address_trans ( Constant mem_length : IN POSITIVE; + Constant addr : IN bit_vector + ) return NATURAL is + + Variable nad, power : NATURAL; + Variable vect_size : integer := vector_size(mem_length - 1); + Variable talias_addr : bit_vector(addr'length - 1 downto 0) := addr; + Variable alias_addr : bit_vector(vect_size - 1 downto 0) := (others => ADDRESS_X_MAP); + + begin + nad := 0; + power := 1; + alias_addr( minimum(vect_size, addr'length) - 1 downto 0) := + talias_addr( minimum(vect_size,addr'length) - 1 downto 0); + if ( MEM_WARNINGS_ON and (vect_size > addr'length) ) then + assert false + report "Bit width of address vector smaller than that needed to access the entire memory." + & LF & SPACESTR & "Resulting X's being mapped to: " & to_str(ADDRESS_X_MAP) + & LF & SPACESTR & "passed address bit width: " & i_to_str(addr'length) + & LF & SPACESTR & "required address bit width: " & i_to_str(vect_size) + severity WARNING; + elsif ( MEM_WARNINGS_ON and (vect_size < addr'length) ) then + assert false + report "Bit width of address vector larger than that needed to access the entire memory." + & LF & SPACESTR & "passed address bit width: " & i_to_str(addr'length) + & LF & SPACESTR & "required address bit width: " & i_to_str(vect_size) + severity WARNING; + end if; + for i in 0 to vect_size - 1 loop + nad := nad + (power * bit'pos(alias_addr(i))); + power := power * 2; + end loop; + return nad; + end; + + + --------------------------------------------------------------------------- + -- Procedure Name : allocate_row + -- + -- Purpose : to allocate a row of memory and initialize it + -- to the default value + -- + -- Parameters : mem_id - ptr to memory data structure + -- row - row to be allocated + -- + -- NOTE : allocate data space for 1 row of memory + -- ****** this procedure is NOT user visible ******* + -- + -- Use : allocate_row (ram1, 5); + --------------------------------------------------------------------------- + + procedure allocate_row ( Variable mem_id : INOUT mem_id_type; + Constant row : IN NATURAL + ) is + + subtype constrained_matrix is + row_matrix (0 to mem_id.columns-1, 0 to mem_id.width-1); + variable ptr : rowptr_type; + variable i, j : integer; + + begin + + if mem_id.row_data_ptr(row).all_xs then -- if row should be filled with X's then do so + + mem_id.row_data_ptr(row).rowptr := new constrained_matrix'( others => (others => 'X')); + + else -- otherwise, row should be filled with the default + + mem_id.row_data_ptr(row).rowptr := new constrained_matrix; + + ptr := mem_id.row_data_ptr(row).rowptr; + for i in 0 to mem_id.columns - 1 loop + for j in 0 to mem_id.width - 1 loop + ptr(i,j) := To_UX01(mem_id.default(j)); + end loop; + end loop; + end if; + -- no longer necessary to indicate that its filled with X's + mem_id.row_data_ptr(row).all_xs := FALSE; + end; + + --------------------------------------------------------------------------- + -- Procedure Name : validate_row + -- + -- Purpose : if memory is a DRAM then check if refresh period + -- has expired. If so, and space allocated, then + -- reset all locations to X's. This is done by setting + -- the filed all_xs to TRUE + -- + -- Parameters : mem_id - pointer to memory data structure + -- row - row to be validated + -- + -- NOTE : ****** this procedure is NOT user visible ******* + -- + -- Use : validate_row (dram1, 5); + --------------------------------------------------------------------------- + + Procedure validate_row ( Variable mem_id : INOUT mem_id_type; + Constant row : IN NATURAL + ) IS + + Variable rowdat : row_data_ptr_type := mem_id.row_data_ptr; + Variable i, j : INTEGER; + + begin + -- check that it is a dram and that refresh period has expired + if ( (mem_id.memory_type = DRAM) and (NOW > (rowdat(row).last_refresh + mem_id.refresh_period)) ) then + if rowdat(row).all_xs then + -- if all_xs is true already then only an assertion is necessray + assert NOT MEM_WARNINGS_ON + report "Refresh time has expired on row " & i_to_str(row) & " of memory: " + & pstr(mem_id.name(1 to mem_id.name'length)) & LF & SPACESTR & "however, row was not filled with valid data." + severity WARNING; + elsif rowdat(row).rowptr = NULL then + -- if all_xs is false and no space has been allocated for this row then it must be at default + -- set all_xs to true and make an assertion + rowdat(row).all_xs := TRUE; + assert NOT MEM_WARNINGS_ON + report "Refresh time has expired on row " & i_to_str(row) & " of memory: " + & pstr(mem_id.name(1 to mem_id.name'length)) & LF & SPACESTR & "Row was filled with default value." + severity WARNING; + else + -- row has valid, non-default data in it + -- set all_xs to true and deallocate space for row + rowdat(row).all_xs := TRUE; + deallocate(mem_id.row_data_ptr(row).rowptr); + mem_id.row_data_ptr(row).rowptr := NULL; + assert NOT MEM_WARNINGS_ON + report "Refresh time has expired on row " & i_to_str(row) & " of memory: " + & pstr(mem_id.name(1 to mem_id.name'length)) & LF & SPACESTR & "Data is lost." + severity WARNING; + end if; + end if; + end; + + --------------------------------------------------------------------------- + -- Procedure Name : refresh_row + -- + -- Purpose : if memory is a DRAM then update the last_refresh + -- time along with last time used (last_init) + -- + -- Parameters : mem_id - pointer to memory data structure + -- row - row to be refreshed + -- + -- NOTE : ****** this procedure is NOT user visible ******* + -- + -- Use : refresh_row (dram1, 5); + --------------------------------------------------------------------------- + + Procedure refresh_row ( VARIABLE mem_id : INOUT mem_id_type; + Constant row : IN NATURAL + ) is + + begin + if ( (mem_id.memory_type = DRAM) and (mem_id.last_init + mem_id.refresh_period >= NOW)) then + mem_id.row_data_ptr(row).last_refresh := NOW; + mem_id.last_init := NOW; + end if; + end; + + --------------------------------------------------------------------------- + -- Function Name : SRAM_Initialize + -- + -- Purpose : To create the data structure used to store a + -- static RAM and to initialize it + -- + -- Parameters : name - string used to represent the memory + -- length - the number of "words" in the memory + -- width - the length of a "word" of memory + -- default_word - value to which each word of + -- memory should be initialized + -- + -- RETURNED VALUE : mem_id_type - pointer to memory record + -- + -- NOTE : initially the data structure is empty with no + -- space being allocated for the memory + -- + -- Use : SRAM_Initialize (sram_l1,"lsb_of_RAM",1048576,1); + --------------------------------------------------------------------------- + + impure Function SRAM_Initialize ( Constant name : IN string; + Constant length : IN POSITIVE; + Constant width : IN POSITIVE; + Constant default_word : IN std_logic_vector + ) return mem_id_type IS + + Variable i, name_len : INTEGER; + Variable mem_id : mem_id_type; + Variable alias_name : string (1 to name'length) := name; + + begin + -- create and initialize data structure + + mem_id := new mem_id_rtype '( memory_type => SRAM, + refresh_period => 0.0 ns, + last_init => 0.0 ns, + counter => 0, + name => NULL, + rows => 1, + columns => SRAM_COL_SIZE, + width => width, + length => length, + row_data_ptr => NULL, + default => NULL + ); + + if ( (length mod SRAM_COL_SIZE) /= 0) then + mem_id.rows := (length/SRAM_COL_SIZE) + 1; + else + mem_id.rows := length/SRAM_COL_SIZE; + end if; + -- store name of memory + name_len := 1; + while ( (name_len <= alias_name'length) and (alias_name(name_len) /= nul)) loop + name_len := name_len + 1; + end loop; + name_len := name_len - 1; + + mem_id.name := new string(1 to name_len); + + for i in 1 to name_len loop + mem_id.name(i) := alias_name(i); + end loop; + -- create and initialize data structure for rows + + mem_id.row_data_ptr := new row_data(0 to mem_id.rows-1); + + for i in 0 to mem_id.rows - 1 loop + mem_id.row_data_ptr(i) := (last_refresh => NOW, + rowptr => NULL, + all_xs => FALSE + ); + end loop; + -- set default word + + mem_id.default := new std_logic_vector(mem_id.width - 1 downto 0); + + if (default_word'length /= mem_id.width) then + assert (default_word'length = 0) + report "SRAM_INITIALIZE: Default word width does not match word width of memory: " + & pstr(mem_id.name(1 to mem_id.name'length)) & LF & SPACESTR + & "default will be set to a word filled with 'U'" + severity ERROR; + for i in 0 to mem_id.width - 1 loop + mem_id.default(i) := 'U'; + end loop; + else + + mem_id.default.all := To_X01(default_word); + + end if; + return mem_id; + end; + + + impure Function SRAM_Initialize ( Constant name : IN string; + Constant length : IN POSITIVE; + Constant width : IN POSITIVE; + Constant default_word : IN std_ulogic_vector + ) return mem_id_type IS + + Variable mem_id : mem_id_type; + + begin + mem_id := SRAM_Initialize ( name, + length, + width, + std_logic_vector (default_word) + ); + return mem_id; + end; + + + --------------------------------------------------------------------------- + -- Procedure Name : Mem_Wake_Up + -- + -- Purpose : to initialize a DRAM for use + -- + -- Parameters : mem_id - ptr to memory data structure + -- + -- NOTE : a DRAM must be woken up before it can be used or if + -- the refresh period passes without any operations + -- + -- Use : Mem_Wake_Up (ROM_chip_1); + --------------------------------------------------------------------------- + + Procedure Mem_Wake_Up (Variable mem_id : INOUT mem_id_type) IS + + begin + if (mem_id.memory_type = DRAM) then + mem_id.last_init := NOW; + else + assert false + report "Mem_Wake_Up: Memory: " & pstr(mem_id.name(1 to mem_id.name'length)) & " is a ROM or an SRAM." + & LF & SPACESTR & + "This operation only valid for DRAM's,operation ignored." + severity ERROR; + end if; + end; + + + + --------------------------------------------------------------------------- + -- Procedure Name : Mem_Basic_Write + -- + -- Purpose : To write a "word" to memory + -- this procedure will write to a ROM + -- + -- Parameters : mem_id - ptr to memory data structure + -- address - address to read from + -- data - "word" to be written to memory + -- must first be converted to X01 + -- ingore_rom - if true then write even if ROM + -- + -- NOTE : a write refreshes row of a DRAM + -- ***** this procedure not user visible ******* + -- + --------------------------------------------------------------------------- + + + Procedure Mem_Basic_Write ( Variable mem_id : INOUT mem_id_type; + Constant address : IN NATURAL; + Constant data : IN std_logic_vector; + Constant ignore_rom : IN Boolean := FALSE + ) IS + + Constant alias_data : std_logic_vector (data'length - 1 downto 0) := data; + variable row, column, i : integer; + variable short_ptr : rowptr_type; + variable mem_word : std_logic_vector (mem_id.width - 1 downto 0) + := (others => 'X'); + + begin + if ( (mem_id.memory_type /= ROM) or (ignore_rom) ) then -- make sure its not a rom + if address < mem_id.length then -- check that its a valid address + -- if memory is a dram make sure that it has been woken up + if ( (mem_id.memory_type /= DRAM) or (mem_id.last_init + mem_id.refresh_period >= NOW)) then + -- calculate row and column + row := address/mem_id.columns; + column := address mod mem_id.columns; + -- validate address and report if refresh time exceeded + validate_row (mem_id, row); + -- refresh the row + refresh_row (mem_id, row); + -- if row never allocated then allocate it + if (mem_id.row_data_ptr(row).rowptr = NULL) then + allocate_row(mem_id, row); + end if; + -- handle data of different width than memory + -- if data has less bits than memory than MSBs become Xs + assert ( (data'length = mem_id.width) OR NOT MEM_WARNINGS_ON) + report "Mem_Write: passed data size does not match word size" + & " of mem: " & pstr(mem_id.name(1 to mem_id.name'length)) & LF & SPACESTR & + "passed data size: " & i_to_str(data'length) & " bits" + & LF & SPACESTR & "memory word size: " & + i_to_str(mem_id.width) & " bits" + severity WARNING; + if (mem_id.width >= data'length) then + mem_word (data'length - 1 downto 0) := alias_data; + else + mem_word := To_X01(alias_data(mem_id.width-1 downto 0)); + end if; + -- write data to memory + short_ptr := mem_id.row_data_ptr(row).rowptr; + for i IN 0 to mem_id.width - 1 loop + -- mem_id.row_data_ptr(row).rowptr(column,i) := mem_word(i); + -- ************************************* + -- this is a bug work around for synopsys + -- replaces line commented out above + short_ptr(column,i) := mem_word(i); + -- end bug fix + -- ************************************* + end loop; + else + assert false + report "Mem_Write: Device wake-up time limit exceeded for memory: " + & pstr(mem_id.name(1 to mem_id.name'length)) & LF & SPACESTR & + "Operation ignored, device must be woken up." + severity WARNING; + end if; + else + assert false + report "Mem_Write: Passed address exceeds address " + & "range of mem: " & pstr(mem_id.name(1 to mem_id.name'length)) & LF & SPACESTR + & "specified address: " & i_to_str(address) & LF & + SPACESTR & "address range: 0 to " & i_to_str(mem_id.length - 1) + severity ERROR; + end if; + else + assert false + report "Mem_Write: Attempt to write to memory: " & pstr(mem_id.name(1 to mem_id.name'length)) + & LF & SPACESTR & "Writes to ROMs are not allowed. Operation ignored." + severity ERROR; + end if; + end; + + + --------------------------------------------------------------------------- + -- Procedure Name : Mem_All_Reset + -- + -- Purpose : To set the contents of a memory to some predetermined + -- value. The locations to reset are specified by a + -- range. + -- + -- Parameters : mem_id - ptr to memory data structure + -- reset_value - value to reset memory to + -- start_addr - starting address within memory + -- end_addr - ending address withim memory + -- ROM_too - allows roms to be reset as well if true + -- + -- NOTE : works for all mem types. call by Mem_Reset + -- **** NOT USER VISIBLE ***** + -- + -- Use : Mem_ALL_Reset (RAM1, "1010", 2048, 4096, FALSE); + --------------------------------------------------------------------------- + + procedure Mem_ALL_Reset ( Variable mem_id : INOUT mem_id_type; + Constant reset_value : IN std_logic_vector; + Constant start_addr : IN NATURAL := 0; + Constant end_addr : IN NATURAL := integer'high; + Constant ROM_too : IN BOOLEAN := FALSE + ) IS + + Variable real_end : NATURAL := end_addr; + Variable start_row, start_col, end_row, end_col : NATURAL; + Variable row, col, rstart_col, rend_col, bit_pos : NATURAL; + Variable row_ptr : rowptr_type; + Variable alias_reset : std_logic_vector (mem_id.width - 1 downto 0) := (others => 'U'); + Variable xvector : std_logic_vector (mem_id.width - 1 downto 0) := (others => 'X'); + Variable i : integer; + + begin + if (reset_value'length /= mem_id.width) then + assert (reset_value'length <= 0) + report "Mem_Reset: reset value of memory does not match memory width " & + pstr(mem_id.name(1 to mem_id.name'length)) & LF & SPACESTR + & "Resetting memory all all 'U's." + severity ERROR; + alias_reset := (others => 'U'); + else + alias_reset := To_X01(reset_value); + end if; + if ( (mem_id.memory_type /= ROM) or ROM_too) then -- make sure its not a rom + if (end_addr < start_addr) then -- check address ranges + assert false + report "Mem_Reset: ending address is less than starting address." + & LF & SPACESTR & "No operation performed." + severity ERROR; + elsif (start_addr >= mem_id.length) then + assert false + report "Mem_Reset: starting address outside of address " + & "range of memory: " & pstr(mem_id.name(1 to mem_id.name'length)) + & LF & SPACESTR & "No operation performed." + severity ERROR; + else + If (end_addr >= mem_id.length) then + assert (end_addr = integer'high) + report "Mem_Reset: ending address outside address " + & "range of memory: " & pstr(mem_id.name(1 to mem_id.name'length)) & LF & SPACESTR + & "Memory will be refreshed until end is reached." + severity WARNING; + real_end := mem_id.length - 1; + end if; + -- if memory is a dram, then wake it up + if mem_id.memory_type = DRAM then + Mem_Wake_Up (mem_id); + end if; + -- calculate row and column of starting address + start_row := start_addr/mem_id.columns; + start_col := start_addr mod mem_id.columns; + -- calculate row and column of ending address + end_row := real_end/mem_id.columns; + end_col := real_end mod mem_id.columns; + -- starting column of row presently being written to + rstart_col := start_col; + for row in start_row to end_row loop + if row = end_row then -- set ending collumn of row presently being written to + rend_col := end_col; + else + rend_col := mem_id.columns - 1; + end if; + -- check for expired time period on row + if ( (rstart_col > 0) or (rend_col < mem_id.columns - 1) ) then + -- it is only necessary to validate row if only part of the row is being reset + validate_row (mem_id, row); + else + -- entire row being reset, check for expired refresh period + assert ( (mem_id.memory_type /= DRAM) or + ((mem_id.row_data_ptr(row).last_refresh + mem_id.refresh_period) >= NOW) + or NOT MEM_WARNINGS_ON ) + report "Mem_Reset: refresh period on row " & + i_to_str(row) & " has expired but" + & LF & SPACESTR + & "no data lost since entire row is being reset" + severity WARNING; + end if; + -- if collumn not allocated & fill value is not the default or all x's then allocate + + if ( (mem_id.row_data_ptr(row).rowptr = NULL) and (alias_reset /= mem_id.default.all) + and (alias_reset /= xvector) ) then + + allocate_row (mem_id, row); + -- if filling partial row with default and currently is Xs then allocate + + elsif ( (mem_id.row_data_ptr(row).rowptr = NULL) and (alias_reset = mem_id.default.all) + and mem_id.row_data_ptr(row).all_xs and + ( (rstart_col /= 0) or (rend_col /= mem_id.columns - 1)) ) then + + allocate_row (mem_id, row); + -- if filling partial row with Xs and currently is default then allocate + elsif ( (mem_id.row_data_ptr(row).rowptr = NULL) and (alias_reset = xvector) + and (NOT mem_id.row_data_ptr(row).all_xs) and + ( (rstart_col /= 0) or (rend_col /= mem_id.columns - 1)) ) then + allocate_row (mem_id, row); + end if; + -- if filling entire collumn with default then deallocate it + + If ( (alias_reset = mem_id.default.all) and (rstart_col = 0) and + + (rend_col = mem_id.columns - 1) ) then + if (mem_id.row_data_ptr(row).rowptr /= NULL) then + Deallocate (mem_id.row_data_ptr(row).rowptr); + mem_id.row_data_ptr(row).rowptr := NULL; + end if; + mem_id.row_data_ptr(row).all_xs := FALSE; + -- if filling entire collumn with X's then deallocate it + elsif ( (alias_reset = xvector) and (rstart_col = 0) and (rend_col = mem_id.columns - 1) ) then + if (mem_id.row_data_ptr(row).rowptr /= NULL) then + Deallocate (mem_id.row_data_ptr(row).rowptr); + mem_id.row_data_ptr(row).rowptr := NULL; + end if; + mem_id.row_data_ptr(row).all_xs := TRUE; + end if; + -- fill up the row if the entire row isn't being filled with Xs or default + row_ptr := mem_id.row_data_ptr(row).rowptr; + if (row_ptr /= NULL) then + for col in rstart_col to rend_col loop + for bit_pos in 0 to mem_id.width - 1 loop + row_ptr(col,bit_pos) := alias_reset(bit_pos); + end loop; + end loop; + end if; + rstart_col := 0; -- start at beginning of next collumn + refresh_row (mem_id, row); -- refresh the current row + end loop; + end if; + else + assert false + report "Mem_Reset: Reset of ROM not allowed. Operation ignored" + severity ERROR; + end if; + end; + + + -------------------------------------------------------------------------------------------------- + -- The following functions and procedures are used in the recursive descent parser that is used + -- to parse the memory files. + -- ***************************** THESE ROUTINES ARE NOTE USER VISIBLE *********************** + -------------------------------------------------------------------------------------------------- + + + -- return true if character is upper case character + function is_upper_case ( Constant ch : IN CHARACTER ) return BOOLEAN is + + begin + return ( (ch >= 'A') and (ch <= 'Z') ); + end; + + -- return true if character is lower case character + function is_lower_case ( Constant ch : IN CHARACTER ) return BOOLEAN is + + begin + return ( (ch >= 'a') and (ch <= 'z') ); + end; + + -- return true if character is a decimal digit + function is_dec_digit ( Constant ch : IN CHARACTER ) return BOOLEAN is + + begin + return ( (ch >= '0') and (ch <= '9')); + end; + + + -- skip over blanks and tabs + -- read characters and numbers until space, tab, or symbol is encountered + -- get special identifiers such as :, --, or .. + -- convert lower case to upper case + -- update buffer index to point to first character after identifier that was read + + procedure read_word ( Variable out_str : OUT STRING; + Constant in_str : IN STRING; + Variable b_ind : INOUT INTEGER + ) is + + Variable out_ind : integer := 1; + + begin + -- skip over spaces and tabs + while ( ( (in_str(b_ind) = ' ') or (in_str(b_ind) = HT) ) and (b_ind <= StrLen1(in_str))) loop + b_ind := b_ind + 1; + end loop; + if ( b_ind > StrLen1(in_str) ) then -- return if blank line + out_str(out_ind) := NUL; + return; + end if; + -- check for -- + if ( (StrLen1(in_str) >= b_ind+1) and (in_str(b_ind) = '-') and (in_str(b_ind + 1) = '-') ) then + out_str(1) := '-'; + out_str(2) := '-'; + out_str(3) := NUL; + b_ind := b_ind + 2; + return; + end if; + -- check for .. + if ( (StrLen1(in_str) >= b_ind+1) and (in_str(b_ind) = '.') and (in_str(b_ind + 1) = '.') ) then + out_str(1) := '.'; + out_str(2) := '.'; + out_str(3) := NUL; + b_ind := b_ind + 2; + return; + end if; + -- check for : + if ( (StrLen1(in_str) >= b_ind) and (in_str(b_ind) = ':') ) then + out_str(1) := ':'; + out_str(2) := NUL; + b_ind := b_ind + 1; + return; + end if; + -- get an identifier + loop -- accept at least one character no matter what is is + if ( (in_str(b_ind) >= 'a') and (in_str(b_ind) <= 'z') ) then + out_str(out_ind) := Character'Val( (Character'POS(in_str(b_ind))) - 32 ); -- convert to upper case + else + out_str(out_ind) := in_str(b_ind); + end if; + out_ind := out_ind + 1; + b_ind := b_ind + 1; + exit when ( (b_ind > StrLen1(in_str)) or + not (is_upper_case (in_str(b_ind)) or is_lower_case(in_str(b_ind)) or + is_dec_digit(in_str(b_ind)) + ) + ); + end loop; + out_str(out_ind) := NUL; + end; + + -- make sure string is a valid hexadecimal number + + Function valid_hex ( Constant str : IN STRING ) return BOOLEAN is + + variable i : integer; + variable valid : BOOLEAN := TRUE; + + begin + i := 1; + while ( (i <= StrLen1(str)) and valid ) loop + valid := ( (str(i) >= '0') and (str(i) <= '9') ) or + ( (str(i) >= 'a') and (str(i) <= 'z') ) or + ( (str(i) >= 'A') and (str(i) <= 'Z') ); + i := i + 1; + end loop; + return valid; + end; + + -- determine what kind of identifier the sring is + Function word_id (Constant str : IN STRING) return IDENTIFIER is + + begin + if StrLen1(str) = 0 then + -- assert false report "BLANK1" severity NOTE; + return BLANK1; + elsif ( (StrLen1(str) = 1) and (str(1) = ':') ) then + -- assert false report "COLON1" severity NOTE; + return COLON1; + elsif ( (StrLen1(str) = 2) and (str(1) = '-') and (str(2) = '-') ) then + -- assert false report "COMMENT1" severity NOTE; + return COMMENT1; + elsif ( (StrLen1(str) = 2) and (str(1) = '.') and (str(2) = '.') ) then + -- assert false report "DOTDOT1" severity NOTE; + return DOTDOT1; + elsif ( (StrLen1(str) = 5) and (str(1 to 5) = "WIDTH") ) then + -- assert false report "WIDTH1" severity NOTE; + return WIDTH1; + elsif ( (StrLen1(str) = 7) and (str(1 to 7) = "DEFAULT") ) then + -- assert false report "DEFAULT1" severity NOTE; + return DEFAULT1; + elsif (valid_hex(str)) then + -- assert false report "HEX_NUM1" severity NOTE; + return HEX_NUM1; + else + -- assert false report "SYN_ERROR1" severity NOTE; + return SYN_ERROR1; + end if; + end; + + -- force the parser to start parsing from the next line. + -- Reset the string buffer index to the first element + procedure new_line ( Variable str_buff : INOUT string; + Variable b_index : INOUT integer; + Variable file_error : INOUT integer; + Variable in_file : IN TEXT; + Variable line_num : INOUT integer + ) is + + Variable line_ptr : LINE; + + begin + b_index := 1; + if not endfile(in_file) then + fgetline1(str_buff, in_file, line_ptr); + line_num := line_num + 1; + else + file_error := 1; + end if; + DEALLOCATE(line_ptr); + end; + + -- get the next symbol in the file going to the next line if necessary + + procedure get_sym ( Variable word : INOUT string; + Variable str_buff : INOUT string; + Variable b_index : INOUT integer; + Variable file_error : INOUT integer; + Variable in_file : IN TEXT; + Variable line_num : INOUT integer + ) is + + Variable line_ptr : LINE; + + begin + if ( b_index > StrLen1(str_buff) ) then -- if end of line reached then get another line + b_index := 1; + if not endfile(in_file) then + fgetline1(str_buff, in_file, line_ptr); + line_num := line_num + 1; + else + file_error := 1; + end if; + word(1) := NUL; + else + read_word(word, str_buff, b_index); + end if; + DEALLOCATE(line_ptr); + end; + + -- convert a hexadecimal string to an integer + function from_hex_to_int (word : IN string) return integer is + + variable alias_word : string(1 to word'length) := word; + variable digit, start, leng : integer; + variable power : integer := 1; + variable result : integer := 0; + variable max_bit_num : integer := 0; -- max number of bits needed to represent hex number + + begin + leng := StrLen1(alias_word); + start := 1; + -- eliminate preceeding 0's + while ( (alias_word(start) = '0') and (start < leng) ) loop -- less than leng handles the 0 case + start := start + 1; + end loop; + for i in leng downto start loop + max_bit_num := max_bit_num + 4; + if ( (alias_word(i) >= '0') and (alias_word(i) <= '9')) then + digit := Character'Pos(alias_word(i)) - 48; + else + digit := Character'Pos(alias_word(i)) - 55; + end if; + if ( (max_bit_num >= IntegerBitLength) and (digit > 7) ) then + assert FALSE + report "MemRead: hex value: " & word & " is too large to represent as an integer on this machine" + severity ERROR; + exit; -- exit the loop + end if; + result := result + digit * power; + if (i /= start) then -- power will not be multiplied by 16 on the last iteration. + power := power * 16; -- This will prevent an integer overflow when dealing with + end if; -- the maximum number of hex digits the machine can represent. + end loop; + return result; + end; + + -- parse a width statement + procedure pwidth ( Variable word : INOUT string; + Variable str_buff : INOUT string; + Variable b_index : INOUT integer; + Variable file_error : INOUT integer; + Variable in_file : IN TEXT; + Variable file_width : INOUT integer; + Variable line_num : INOUT integer + ) is + + Variable w_id : IDENTIFIER; + variable error_line : integer; + + begin + w_id := word_id(word); + if (w_id /= WIDTH1) then + file_error := 2; + assert false + report "Mem_Load: Width specification not first executable line in file. File load aborted." + & LF & SPACESTR & "Occurred on line number " & i_to_str(line_num) & " of the input file." + severity ERROR; + else + get_sym (word, str_buff, b_index, file_error, in_file, line_num); + w_id := word_id(word); + if (w_id = COLON1) then + get_sym (word, str_buff, b_index, file_error, in_file, line_num); + w_id := word_id(word); + if w_id = HEX_NUM1 then + file_width := from_hex_to_int(word); + get_sym (word, str_buff, b_index, file_error, in_file, line_num); + w_id := word_id(word); + error_line := line_num; + if w_id = COMMENT1 then + new_line (str_buff, b_index, file_error, in_file, line_num); + elsif w_id /= BLANK1 then + assert false + report "Mem_Load: Additional information on width specification line ignored." + & LF & SPACESTR & "File processing continuing." + & LF & SPACESTR & "Occurred on line number " & i_to_str(line_num) + & " of the input file." + severity ERROR; + new_line(str_buff, b_index, file_error, in_file, line_num); + end if; + if file_width = 0 then + file_error := 10; + assert false + report "Mem_load: Width must be greater than 0. File load aborted." + & LF & SPACESTR & "Occurred on line number " & i_to_str(error_line) + & " of the input file." + severity ERROR; + end if; + else + file_error := 3; + end if; + else + file_error := 3; + end if; + end if; + if file_error = 3 then + assert false + report "Mem_load: Syntax error in width specification. File load aborted." + & LF & SPACESTR & "Occurred on line number " & i_to_str(line_num) & " of the input file." + severity ERROR; + end if; + end; + + -- parse a default statement + procedure pdefault ( Variable word : INOUT string; -- present word + Variable str_buff : INOUT string; -- string buffer + Variable b_index : INOUT integer; -- string buffer index + Variable file_error : INOUT integer; -- error? + Variable in_file : IN TEXT; -- file + Variable file_width : IN integer; -- width specified by file + Variable mem_id : INOUT mem_id_type; -- memory + Constant hex_size : IN integer; -- number of hex digits expected + Constant rwidth : IN integer; -- # of bits to be written to memory + Variable line_num : INOUT integer -- line # of file + ) is + + Variable w_id : IDENTIFIER; + Variable tdata : bit_vector (file_width - 1 downto 0); + Variable data : std_logic_vector (mem_id.width - 1 downto 0); + + + begin + get_sym (word, str_buff, b_index, file_error, in_file, line_num); + w_id := word_id(word); + if w_id = COLON1 then + get_sym (word, str_buff, b_index, file_error, in_file, line_num); + w_id := word_id(word); + if w_id = HEX_NUM1 then + if StrLen1(word) = hex_size then + data := (others => 'X'); + tdata := From_HexString1(word)(file_width - 1 downto 0); + data(rwidth - 1 downto 0) := bv_To_StdLogicvector(tdata(rwidth - 1 downto 0)); + if mem_id.default = NULL then + + mem_id.default := new std_logic_vector(mem_id.width - 1 downto 0); + + else + deallocate (mem_id.default); + + mem_id.default := new std_logic_vector(mem_id.width - 1 downto 0); + + end if; + + mem_id.default.all := data; + + get_sym (word, str_buff, b_Index, file_error, in_file, line_num); + w_id := word_id(word); + if w_id = COMMENT1 then + new_line (str_buff, b_index, file_error, in_file, line_num); + elsif w_id /= BLANK1 then + assert false + report "Mem_Load: Additional information on default specification line ignored." + & LF & SPACESTR & "File processing continuing." + & LF & SPACESTR & "Occurred on line number " & i_to_str(line_num) + & " of the input file." + severity ERROR; + new_line(str_buff, b_index, file_error, in_file, line_num); + end if; + else + assert false + report "Mem_Load: Default word length does not match file specification for width of memory. " + & " Default ignored." + & LF & SPACESTR & "Occurred on line number " & i_to_str(line_num) + & " of the input file." + severity ERROR; + new_line (str_buff, b_index, file_error, in_file, line_num); + end if; + + else + file_error := 4; + end if; + else + file_error := 4; + end if; + if file_error = 4 then + assert false + report "Mem_Load: Syntax error in default word specification. Line ignored." + & LF & SPACESTR & "Occurred on line number " & i_to_str(line_num) & " of the input file." + severity ERROR; + new_line (str_buff, b_index, file_error, in_file, line_num); + file_error := 0; + end if; + end; + + -- parse an assignment statement + procedure passign ( Variable addr1 : IN integer; -- starting address of assignment + Variable word : INOUT string; -- current word + Variable str_buff : INOUT string; -- string buffer + Variable b_index : INOUT integer; -- string buffer index + Variable file_error : INOUT integer; -- error? + Variable in_file : IN TEXT; -- file + Variable file_width : IN integer; -- width specified by file + Variable mem_id : INOUT mem_id_type; -- memory + Constant hex_size : IN integer; -- number of hex digits expected + Constant rwidth : IN integer; -- # of bits to be written to memory + Variable line_num : INOUT integer -- line # of file + ) is + + Variable addr : integer := addr1; + Variable w_id : IDENTIFIER := COLON1; + Variable tdata : bit_vector (file_width - 1 downto 0); + Variable data : std_logic_vector (mem_id.width - 1 downto 0); + + begin + while ( (file_error = 0) and (w_id /= BLANK1) and (w_id /= COMMENT1) ) loop + get_sym (word, str_buff, b_Index, file_error, in_file, line_num); + w_id := word_id(word); + if w_id = HEX_NUM1 then + if StrLen1(word) = hex_size then + data := (others => 'X'); + tdata := From_HexString1(word)(file_width - 1 downto 0); + data(rwidth - 1 downto 0) := bv_To_StdLogicVector(tdata(rwidth - 1 downto 0)); + Mem_Basic_Write (mem_id, addr, data, TRUE); + else + assert false + report "Mem_Load: Data word length does not match width specification on line: " + & i_to_str(line_num) & "." & LF & SPACESTR + & "Data byte skipped." + severity ERROR; + end if; + addr := addr + 1; + elsif ( (w_id /= BLANK1) and (w_id /= COMMENT1) ) then + file_error := 5; + assert false + report "Mem_Load: Syntax error on assignment line. Line processed up to error." & + LF & SPACESTR & "Occurred on line number " & i_to_str(line_num) & " of the input file." + severity ERROR; + new_line (str_buff, b_index, file_error, in_file, line_num); + end if; + end loop; + if w_id = COMMENT1 then + new_line (str_buff, b_index, file_error, in_file, line_num); + end if; + if file_error = 5 then + file_error := 0; + end if; + end; + + -- parse a range assignment + procedure prange ( Variable addr1 : IN integer; -- strarting address + Variable word : INOUT string; -- current word + Variable str_buff : INOUT string; -- string buffer + Variable b_index : INOUT integer; -- string buffer index + Variable file_error : INOUT integer; -- error? + Variable in_file : IN TEXT; -- file + Variable file_width : IN integer; -- width specified in file + Variable mem_id : INOUT mem_id_type; -- memory + Constant hex_size : IN integer; -- number of hex digits expected + Constant rwidth : IN integer; -- # of bits to be written to memory + Variable line_num : INOUT integer -- line # of file + ) is + + Variable addr2, addr : integer; + Variable w_id : IDENTIFIER; + Variable tdata : bit_vector (file_width - 1 downto 0); + Variable data : std_logic_vector (mem_id.width - 1 downto 0); + Variable error_line : integer; + + begin + get_sym (word, str_buff, b_index, file_error, in_file, line_num); + w_id := word_id(word); + if w_id = HEX_NUM1 then + addr2 := from_hex_to_int(word); + if addr2 < addr1 then + file_error := 7; + error_line := line_num; + new_line (str_buff, b_index, file_error, in_file, line_num); + else + get_sym (word, str_buff, b_index, file_error, in_file, line_num); + w_id := word_id(word); + if w_id = COLON1 then + get_sym (word, str_buff, b_index, file_error, in_file, line_num); + w_id := word_id(word); + if w_id = HEX_NUM1 then + if StrLen1(word) = hex_size then + data := (others => 'X'); + tdata := From_HexString1(word)(file_width - 1 downto 0); + data(rwidth - 1 downto 0) := bv_To_StdLogicVector(tdata(rwidth - 1 downto 0)); + Mem_ALL_Reset (mem_id, data, addr1, addr2, TRUE); + get_sym (word, str_buff, b_Index, file_error, in_file, line_num); + w_id := word_id(word); + if w_id = COMMENT1 then + new_line (str_buff, b_index, file_error, in_file, line_num); + elsif w_id /= BLANK1 then + assert false + report "Mem_Load: Additional information on range assignment line ignored." + & LF & SPACESTR & "File processing continuing." + & LF & SPACESTR & "Occurred on line number " & i_to_str(line_num) + & " of the input file." + severity ERROR; + new_line(str_buff, b_index, file_error, in_file, line_num); + end if; + else + assert false + report "Mem_Load: Data word length does not match width specification." + & LF & SPACESTR & "Line skipped" + & LF & SPACESTR & "Occurred on line number " & i_to_str(line_num) + & " of the input file." + severity ERROR; + new_line(str_buff, b_index, file_error, in_file, line_num); + end if; + addr := addr + 1; + else + assert false + report "Mem_Load: Syntax Error on range assignment line. Line skipped." + & LF & SPACESTR & "Occurred on line number " & i_to_str(line_num) + & " of the input file." + severity ERROR; + new_line(str_buff, b_index, file_error, in_file, line_num); + end if; + -- get_sym (word, str_buff, b_index, file_error, in_file, line_num); + -- w_id := word_id(word); + else + file_error := 6; + error_line := line_num; + new_line (str_buff, b_index, file_error, in_file, line_num); + end if; + end if; + else + file_error := 6; + new_line (str_buff, b_index, file_error, in_file, line_num); + end if; + if file_error = 7 then + assert false + report "Mem_load: Addr2 < Addr1 in range specification. Line skipped." + & LF & SPACESTR & "Occurred on line number " & i_to_str(error_line) & " of the input file." + severity ERROR; + file_error := 0; + end if; + if file_error = 6 then + assert false + report "Mem_load: Syntax error in range specification. Line skipped." + & LF & SPACESTR & "Occurred on line number " & i_to_str(error_line) & " of the input file." + severity ERROR; + file_error := 0; + end if; + end; + + -- decide if current statement is a simple assignment of a range assignment + -- then parse that statment + procedure p_op_statement ( Variable word : INOUT string; + Variable str_buff : INOUT string; + Variable b_index : INOUT integer; + Variable file_error : INOUT integer; + Variable in_file : IN TEXT; + Variable file_width : IN integer; + Variable mem_id : INOUT mem_id_type; + Constant hex_size : IN integer; + Constant rwidth : IN integer; + Variable line_num : INOUT integer + ) is + + Variable addr1 : integer; + Variable w_id : IDENTIFIER; + + begin + addr1 := from_hex_to_int(word); + get_sym (word, str_buff, b_index, file_error, in_file, line_num); + w_id := word_id(word); + if w_id = COLON1 then + passign ( addr1, word, str_buff, b_index, file_error, in_file, + file_width, mem_id, hex_size, rwidth, line_num); + elsif w_id = DOTDOT1 then + prange ( addr1, word, str_buff, b_index, file_error, in_file, + file_width, mem_id, hex_size, rwidth, line_num); + else + assert false + report "Mem_Load: Syntax error. Line skipped." + & LF & SPACESTR & "Occurred on line number " & i_to_str(line_num) & " of the input file." + severity ERROR; + new_line (str_buff, b_index, file_error, in_file, line_num); + end if; + end; + + --------------------------------------------------------------------------- + -- Procedure Name : Mem_Basic_Read + -- + -- Purpose : To read a "word" from memory + -- + -- Parameters : data - contents of memory location + -- mem_id - ptr to memory data structure + -- address - address to read from + -- refresh_enable - if true a refresh is performed + -- for DRAMs + -- + -- NOTE : a read refreshes the corresponding row of a DRAM + -- ***** this procedure is not exteranlly visible **** + -- + --------------------------------------------------------------------------- + + + Procedure Mem_Basic_Read ( Variable data : OUT std_logic_vector; + Variable mem_id : INOUT mem_id_type; + Constant address : IN NATURAL; + Constant refresh_enable : IN BOOLEAN := TRUE + ) IS + + Variable alias_data : std_logic_vector(data'length - 1 downto 0) := (others=>'X'); + Variable mem_word : std_logic_vector(mem_id.width - 1 downto 0); + Variable row : NATURAL := address/mem_id.columns; + Variable column : NATURAL := address mod mem_id.columns; + Variable limit : integer; + Variable i : NATURAL; + variable short_ptr : rowptr_type; + + begin + if address < mem_id.length then -- check for valid address range + -- if dram check if woken up and make assertion + -- nothing else has to be done since data will be invalidated due to refresh period violation + assert ( (mem_id.memory_type /= DRAM) or (NOT MEM_WARNINGS_ON) or + ( (mem_id.last_init + mem_id.refresh_period) >= NOW) ) + report "Mem_Read: Device wake-up time limit exceeded for memory: " + & pstr(mem_id.name(1 to mem_id.name'length)) & LF & SPACESTR & + "device must be woken up. All reads will return X's or default word" + severity WARNING; + -- invalidate data if refresh period has expired + validate_row (mem_id, row); + -- now refresh row + if refresh_enable then + refresh_row (mem_id, row); + end if; + -- handle data of different width than memory + assert ( (data'length = mem_id.width) OR NOT MEM_WARNINGS_ON) + report "Mem_Read: return data size does not match word size" + & " of mem: " & pstr(mem_id.name(1 to mem_id.name'length)) & LF & SPACESTR & + "return data size: " & i_to_str(data'length) & " bits" + & LF & SPACESTR & "memory word size: " & + i_to_str(mem_id.width) & " bits" + severity WARNING; + if (mem_id.row_data_ptr(row).all_xs) then -- if all xs then return x's + mem_word := (others => 'X'); + elsif (mem_id.row_data_ptr(row).rowptr = NULL) then -- if not allocated return default + + mem_word := mem_id.default.all; + + else + short_ptr := mem_id.row_data_ptr(row).rowptr; + for i in 0 to mem_id.width - 1 loop -- else return word at that location + -- mem_word(i) := mem_id.row_data_ptr(row).rowptr(column,i); + -- ************************************* + -- this is a bug work around for synopsys + -- replaces line commented out above + mem_word(i) := short_ptr(column,i); + -- end bug fix + -- ************************************* + end loop; + end if; + if mem_id.width >= data'length then + limit := data'length; + else + limit := mem_id.width; + end if; + for i in 0 to limit - 1 loop + alias_data(i) := mem_word(i); + end loop; + else + assert false + report "Mem_Read: Passed address exceeds address " & + "range of mem: " & pstr(mem_id.name(1 to mem_id.name'length)) & LF & SPACESTR + & "specified address: " & i_to_str(address) & LF & + SPACESTR & "address range: 0 to " & i_to_str(mem_id.length - 1) + severity ERROR; + alias_data := (others => 'X'); + end if; + data := alias_data; + end; + + Procedure Mem_Basic_Read ( Variable data : OUT std_ulogic; + Variable mem_id : INOUT mem_id_type; + Constant address : IN NATURAL; + Constant refresh_enable : IN BOOLEAN := TRUE + ) IS + + Variable row : NATURAL := address/mem_id.columns; + Variable column : NATURAL := address mod mem_id.columns; + variable short_ptr : rowptr_type; + + begin + if address < mem_id.length then + assert ( (mem_id.memory_type /= DRAM) or (NOT MEM_WARNINGS_ON) or + ( (mem_id.last_init + mem_id.refresh_period) >= NOW) ) + report "Mem_Read: Device wake-up time limit exceeded for memory: " + & pstr(mem_id.name(1 to mem_id.name'length)) & LF & SPACESTR & + "device must be woken up. All reads will return X's or default word." + severity WARNING; + validate_row (mem_id, row); + if refresh_enable then + refresh_row (mem_id, row); + end if; + -- handle data of different width than memory + assert ( (mem_id.width = 1) OR NOT MEM_WARNINGS_ON) + report "Mem_Read: return data size does not match word size" + & " of mem: " & pstr(mem_id.name(1 to mem_id.name'length)) & LF & + SPACESTR & "return data size: 1" & LF & SPACESTR & "memory word size: " & + i_to_str(mem_id.width) & " bits" + severity WARNING; + if (mem_id.row_data_ptr(row).all_xs) then + data := 'X'; + elsif (mem_id.row_data_ptr(row).rowptr = NULL) then + data := mem_id.default(0); + else + --data := mem_id.row_data_ptr(row).rowptr(column,0); + -- ************************************* + -- this is a bug work around for synopsys + -- replaces line commented out above + short_ptr := mem_id.row_data_ptr(row).rowptr; + data := short_ptr(column,0); + -- end bug fix + -- ************************************* + end if; + else + assert false + report "Mem_Read: Passed address exceeds address " & + "range of mem: " & pstr(mem_id.name(1 to mem_id.name'length)) & LF & SPACESTR + & "specified address: " & i_to_str(address) & LF & + SPACESTR & "address range: 0 to " + & i_to_str(mem_id.length - 1) + severity ERROR; + data := 'X'; + end if; + end; + + + --------------------------------------------------------------------------- + -- Procedure Name : Mem_Read + -- + -- Purpose : To read a "word" from memory + -- + -- Parameters : mem_id - ptr to memory data structure + -- address - address to read from + -- data - contents of memory location + -- + -- + -- NOTE : a read refreshes row of a DRAM + -- + -- Use : Mem_Read (ROM1, "100100111", data); + --------------------------------------------------------------------------- + + Procedure Mem_Read ( Variable mem_id : INOUT mem_id_type; + Constant address : IN std_ulogic_vector; + Variable data : OUT std_ulogic_vector + ) IS + + Variable temp : std_logic_vector(data'length - 1 downto 0); + + begin + Mem_Basic_Read (temp, mem_id, address_trans(mem_id.length, address)); + data := std_ulogic_vector (temp); + end; + + + --------------------------------------------------------------------------- + -- Procedure Name : Mem_Write + -- + -- Purpose : To write a "word" to memory + -- + -- Parameters : mem_id - ptr to memory data structure + -- address - address to read from + -- data - "word" to be written to memory + -- + -- NOTE : a write refreshes row of a DRAM + -- + -- Use : Mem_Write (ROM1, "100100111", "10X1"); + --------------------------------------------------------------------------- + + Procedure Mem_Write ( Variable mem_id : INOUT mem_id_type; + Constant address : IN std_ulogic_vector; + Constant data : IN std_ulogic_vector + ) IS + + begin + Mem_Basic_Write ( mem_id, + Address_trans(mem_id.length, address), + To_X01(std_logic_vector(data)) + ); + end; + + + + + +END mem_model; diff --git a/hdl/gn4124core/sim/gn4124_bfm/textutil.vhd b/hdl/gn4124core/sim/gn4124_bfm/textutil.vhd new file mode 100644 index 0000000000000000000000000000000000000000..1cc3de15f66d170c1333190caae51998a3e40b47 --- /dev/null +++ b/hdl/gn4124core/sim/gn4124_bfm/textutil.vhd @@ -0,0 +1,744 @@ +library IEEE; +use IEEE.std_logic_1164.all; +use std.textio.all; +use work.util.all; + +----------------------------------------------------------------------------- +-- *Module : textutil +-- +-- *Description : Improved Free-format string and line manipulation +-- +-- *History: M. Alford (originaly created 1993 with subsequent updates) +----------------------------------------------------------------------------- +package textutil is + procedure read_token(L : inout line; X : out STRING); + procedure sget_token(S : in string; P : inout integer; X : out STRING); + procedure sget_vector(S : in string; P : inout integer; VEC : out STD_ULOGIC_VECTOR); + procedure sget_vector_64(S : in string; P : inout integer; VEC : out STD_ULOGIC_VECTOR); + procedure sget_int(S : in string; P : inout integer; X : out integer); + function hex_char_to_vector(C : in character) return STD_ULOGIC_VECTOR; + function vector_to_hex_char(V : in STD_ULOGIC_VECTOR) return character; + function is_hex(C : in character) return BOOLEAN; + function hex_char_to_int(C : in character) return integer; + procedure read_vector(L : inout line; VEC : out STD_ULOGIC_VECTOR); + procedure read_int(L : inout line; I : out integer); + procedure write_hex_vector(L : inout line; V : in STD_ULOGIC_VECTOR); + function to_str(constant V: in STD_ULOGIC_VECTOR) return STRING; + function to_str(constant V: in STD_ULOGIC) return STRING; + function to_str(constant val : in INTEGER) return STRING; + function to_strn(constant val : in INTEGER; constant n : in INTEGER) return STRING; +end textutil; + +package body textutil is + +----------------------------------------------------------------------------- +-- *Module : read_token +-- +-- *Description : Skip over spaces then load a token string from a line +-- until either the string is full or the token is finished +-- (i.e. another space). The output string is padded out +-- with blanks at the end if the token length is less then +-- the full string length. +----------------------------------------------------------------------------- + procedure read_token(L : inout line; X : out STRING) is + variable char : character; + begin + if(L'length > 0) then + char := ' '; + while((char = ' ') and (L'length > 0)) loop -- Skip spaces + read(L, char); + end loop; + for i in X'low to X'high loop + X(i) := char; + if(char /= ' ') then + if(L'length > 0) then + read(L, char); + else + char := ' '; + end if; + end if; + end loop; + else + assert false report "Couldn't read a token from file" + severity error; + end if; + end read_token; + +----------------------------------------------------------------------------- +-- *Module : sget_token +-- +-- *Description : Same as read_token except for strings. +----------------------------------------------------------------------------- + procedure sget_token(S : in string; P : inout integer; X : out STRING) is + variable char : character; + begin + if(S'length > P) then + char := ' '; + while((char = ' ') and (S'length >= P)) loop -- Skip spaces + char := S(P); + P := P + 1; + end loop; + for i in X'low to X'high loop + X(i) := char; + if(char /= ' ') then + if(S'length > P) then + char := S(P); + P := P + 1; + else + char := ' '; + end if; + end if; + end loop; + else + assert false report "Couldn't read a token from a string" + severity error; + end if; + end sget_token; + +----------------------------------------------------------------------------- +-- *Module : hex_char_to_vector +-- +-- *Description : Convert a hex character to a vector +----------------------------------------------------------------------------- + function hex_char_to_vector(C : in character) return STD_ULOGIC_VECTOR is + variable X : STD_ULOGIC_VECTOR( 3 downto 0); + begin + case C is + when '0' => X := "0000"; + when '1' => X := "0001"; + when '2' => X := "0010"; + when '3' => X := "0011"; + when '4' => X := "0100"; + when '5' => X := "0101"; + when '6' => X := "0110"; + when '7' => X := "0111"; + when '8' => X := "1000"; + when '9' => X := "1001"; + when 'A' => X := "1010"; + when 'B' => X := "1011"; + when 'C' => X := "1100"; + when 'D' => X := "1101"; + when 'E' => X := "1110"; + when 'F' => X := "1111"; + when 'a' => X := "1010"; + when 'b' => X := "1011"; + when 'c' => X := "1100"; + when 'd' => X := "1101"; + when 'e' => X := "1110"; + when 'f' => X := "1111"; + when others => + X := "0000"; + assert false report "Invalid Hex Character" + severity error; + end case; + return(X); + end hex_char_to_vector; + +----------------------------------------------------------------------------- +-- *Module : vector_to_hex_char +-- +-- *Description : Convert a vector to a hex character. Only uses low 4 bits. +----------------------------------------------------------------------------- + function vector_to_hex_char(V : in STD_ULOGIC_VECTOR) return character is + variable C : character; + variable VV : STD_ULOGIC_VECTOR(3 downto 0); + begin + if(V'length < 4) then + VV := To_X01(V(V'low + 3 downto V'low)); + else + VV := To_X01(V(V'low + V'length - 1 downto V'low)); + end if; + case VV is + when "0000" => C := '0'; + when "0001" => C := '1'; + when "0010" => C := '2'; + when "0011" => C := '3'; + when "0100" => C := '4'; + when "0101" => C := '5'; + when "0110" => C := '6'; + when "0111" => C := '7'; + when "1000" => C := '8'; + when "1001" => C := '9'; + when "1010" => C := 'A'; + when "1011" => C := 'B'; + when "1100" => C := 'C'; + when "1101" => C := 'D'; + when "1110" => C := 'E'; + when "1111" => C := 'F'; + when others => C := 'X'; + end case; + return(C); + end vector_to_hex_char; + +----------------------------------------------------------------------------- +-- *Module : is_hex +-- +-- *Description : report if a char is ASCII hex +----------------------------------------------------------------------------- + function is_hex(C : in character) return BOOLEAN is + variable X : boolean; + begin + case C is + when '0' => X := TRUE; + when '1' => X := TRUE; + when '2' => X := TRUE; + when '3' => X := TRUE; + when '4' => X := TRUE; + when '5' => X := TRUE; + when '6' => X := TRUE; + when '7' => X := TRUE; + when '8' => X := TRUE; + when '9' => X := TRUE; + when 'A' => X := TRUE; + when 'B' => X := TRUE; + when 'C' => X := TRUE; + when 'D' => X := TRUE; + when 'E' => X := TRUE; + when 'F' => X := TRUE; + when 'a' => X := TRUE; + when 'b' => X := TRUE; + when 'c' => X := TRUE; + when 'd' => X := TRUE; + when 'e' => X := TRUE; + when 'f' => X := TRUE; + when others => + X := FALSE; + end case; + return(X); + end is_hex; + +----------------------------------------------------------------------------- +-- *Module : hex_char_to_int +-- +-- *Description : Convert a hex character to an integer +----------------------------------------------------------------------------- + function hex_char_to_int(C : in character) return integer is + variable X : integer; + begin + case C is + when '0' => X := 0; + when '1' => X := 1; + when '2' => X := 2; + when '3' => X := 3; + when '4' => X := 4; + when '5' => X := 5; + when '6' => X := 6; + when '7' => X := 7; + when '8' => X := 8; + when '9' => X := 9; + when 'A' => X := 10; + when 'B' => X := 11; + when 'C' => X := 12; + when 'D' => X := 13; + when 'E' => X := 14; + when 'F' => X := 15; + when 'a' => X := 10; + when 'b' => X := 11; + when 'c' => X := 12; + when 'd' => X := 13; + when 'e' => X := 14; + when 'f' => X := 15; + when others => + X := 0; + assert false report "Invalid Hex Character" + severity error; + end case; + return(X); + end hex_char_to_int; + +----------------------------------------------------------------------------- +-- *Module : read_vector +-- +-- *Description : load a vector from the input line in a free floating format +----------------------------------------------------------------------------- + procedure read_vector(L : inout line; VEC : out STD_ULOGIC_VECTOR) is + variable char : character; + variable base : integer; + variable q : integer; + variable v : STD_ULOGIC_VECTOR(31 downto 0); + begin + if(L'length > 0) then + char := ' '; + while(char = ' ') loop -- Skip spaces + read(L, char); + end loop; + base := 16; -- Hex is the default + if(char = '%') then -- determine base + read(L, char); + if(char = 'b' or char = 'B') then + base := 2; + elsif(char = 'x' or char = 'X') then + base := 16; + elsif(char = 'd' or char = 'D') then + base := 10; + else + assert false report "Unsupported Base detected when reading a Vector" + severity error; + end if; + read(L, char); + end if; + q := 0; + if(is_hex(char)) then + q := q * base + hex_char_to_int(char); + while(is_hex(char) and not (L'length = 0)) loop + read(L, char); + if(is_hex(char)) then + q := q * base + hex_char_to_int(char); + end if; + end loop; + end if; + if(q < 0) then + q := q-2147483648; + V(30 downto 0) := to_vector(q, 31); + V(31) := '1'; + else + V(30 downto 0) := to_vector(q, 31); + V(31) := '0'; + end if; + VEC := V((VEC'high - VEC'low) downto 0); + else + assert false report "Couldn't read a vector" + severity error; + end if; + end read_vector; + +----------------------------------------------------------------------------- +-- *Module : sget_vector +-- +-- *Description : Same as sget_vector except for strings +----------------------------------------------------------------------------- + procedure sget_vector(S : in string; P : inout integer; VEC : out STD_ULOGIC_VECTOR) is + variable char : character; + variable base : integer; + variable q : integer; + variable v : STD_ULOGIC_VECTOR(31 downto 0); + begin + while(S(P) = ' ') loop -- Skip spaces + if(P >= S'length) then + P := S'length; + exit; + end if; + P := P + 1; + end loop; + if(S'length > P) then + char := S(P); + if(char = '"') then -- read in as a literal + q := v'high; + v := (others => 'U'); + VEC := v(VEC'range); + char := ' '; + P := P + 1; + while((char /= '"') and not (S'length = P)) loop + char := S(P); + P := P + 1; + case char is + when '0' => + v(q) := '0'; + when '1' => + v(q) := '1'; + when 'L' | 'l' => + v(q) := 'L'; + when 'H' | 'h' => + v(q) := 'H'; + when 'Z' | 'z' => + v(q) := 'Z'; + when 'X' | 'x' => + v(q) := 'X'; + when 'U' | 'u' => + v(q) := 'U'; + when others => +-- char := '"'; + exit; + end case; + q := q - 1; + end loop; + if(v'high-q < 2) then -- only a single bit was read + VEC(VEC'low) := v(v'high); + elsif((v'high - q) > VEC'length) then -- too many bits + VEC := v(q+VEC'length downto q+1); + else -- the number of bits read is same or less than required + VEC(v'high-q-1+VEC'low downto VEC'low) := v(v'high downto q+1); + end if; + else + base := 16; -- Hex is the default + if(char = '%') then -- determine base + P := P + 1; + char := S(P); + P := P + 1; + if(char = 'b' or char = 'B') then + base := 2; + elsif(char = 'x' or char = 'X') then + base := 16; + elsif(char = 'd' or char = 'D') then + base := 10; + else + assert false report "Unsupported Base detected when reading a Vector" + severity error; + end if; + elsif((char = '0') and ((S(P+1) = 'x') or (S(P+1) = 'X'))) then + P := P + 2; + end if; + q := 0; + char := S(P); + if(is_hex(char)) then + while(is_hex(char) and not (S'length = P)) loop + if(is_hex(char)) then + q := q * base + hex_char_to_int(char); + end if; + P := P + 1; + char := S(P); + end loop; + end if; + if(q < 0) then + q := q-2147483648; + V(30 downto 0) := to_vector(q, 31); + V(31) := '1'; + else + V(30 downto 0) := to_vector(q, 31); + V(31) := '0'; + end if; + VEC := V((VEC'high - VEC'low) downto 0); + end if; + else + assert false report "Couldn't read a vector" + severity error; + V := (others => '0'); + VEC := V((VEC'high - VEC'low) downto 0); + end if; + end sget_vector; + +----------------------------------------------------------------------------- +-- *Module : sget_vector_64 +-- +-- *Description : Same as sget_vector except can handle 64 bit quantities and hex or binary base (no base 10) +----------------------------------------------------------------------------- + procedure sget_vector_64(S : in string; P : inout integer; VEC : out STD_ULOGIC_VECTOR) is + variable char : character; + variable base : integer; + variable q : integer; + variable v : STD_ULOGIC_VECTOR(63 downto 0); + begin + while(S(P) = ' ') loop -- Skip spaces + if(P >= S'length) then + P := S'length; + exit; + end if; + P := P + 1; + end loop; + if(S'length > P) then + char := S(P); + if(char = '"') then -- read in as a literal + q := v'high; + v := (others => 'U'); + VEC := v(VEC'range); + char := ' '; + P := P + 1; + while((char /= '"') and not (S'length = P)) loop + char := S(P); + P := P + 1; + case char is + when '0' => + v(q) := '0'; + when '1' => + v(q) := '1'; + when 'L' | 'l' => + v(q) := 'L'; + when 'H' | 'h' => + v(q) := 'H'; + when 'Z' | 'z' => + v(q) := 'Z'; + when 'X' | 'x' => + v(q) := 'X'; + when 'U' | 'u' => + v(q) := 'U'; + when others => +-- char := '"'; + exit; + end case; + q := q - 1; + end loop; + if(v'high-q < 2) then -- only a single bit was read + VEC(VEC'low) := v(v'high); + elsif((v'high - q) > VEC'length) then -- too many bits + VEC := v(q+VEC'length downto q+1); + else -- the number of bits read is same or less than required + VEC(v'high-q-1+VEC'low downto VEC'low) := v(v'high downto q+1); + end if; + else + base := 16; -- Hex is the default + if(char = '%') then -- determine base + P := P + 1; + char := S(P); + P := P + 1; + if(char = 'b' or char = 'B') then + base := 2; + elsif(char = 'x' or char = 'X') then + base := 16; + else + assert false report "Unsupported Base detected when reading a Vector" + severity error; + end if; + char := S(P); +-- P := P + 1; + elsif((char = '0') and ((S(P+1) = 'x') or (S(P+1) = 'X'))) then + P := P + 2; + end if; + v := (others => '0'); + char := S(P); + if(base = 2) then + while(((char = '0') or (char = '1')) and not (P > S'length)) loop + if(char = '0') then + v := v(v'high-1 downto 0) & '0'; + else + v := v(v'high-1 downto 0) & '1'; + end if; + P := P + 1; + char := S(P); + end loop; + else + while(is_hex(char) and not (P > S'length)) loop + if(is_hex(char)) then + v := v(v'high-4 downto 0) & hex_char_to_vector(char); + end if; + P := P + 1; + char := S(P); + end loop; + end if; + VEC := V((VEC'high - VEC'low) downto 0); + end if; + else + assert false report "Couldn't read a vector" + severity error; + V := (others => '0'); + VEC := V((VEC'high - VEC'low) downto 0); + end if; + end sget_vector_64; + +----------------------------------------------------------------------------- +-- *Module : read_int +-- +-- *Description : load an integer from the input line in a free floating format +----------------------------------------------------------------------------- + procedure read_int(L : inout line; I : out integer) is + variable char : character; + variable base : integer; + variable q : integer; + begin + if(L'length > 0) then + char := ' '; + while(char = ' ') loop -- Skip spaces + read(L, char); + end loop; + base := 16; -- Hex is the default + if(char = '%') then -- determine base + read(L, char); + if(char = 'b' or char = 'B') then + base := 2; + elsif(char = 'x' or char = 'X') then + base := 16; + elsif(char = 'd' or char = 'D') then + base := 10; + else + assert false report "Unsupported Base detected when reading an integer" + severity error; + end if; + read(L, char); + end if; + q := 0; + if(is_hex(char)) then + q := q * base + hex_char_to_int(char); + while(is_hex(char) and not (L'length = 0)) loop + read(L, char); + if(is_hex(char)) then + q := q * base + hex_char_to_int(char); + end if; + end loop; + end if; + I := q; + else + assert false report "Couldn't read an integer" + severity error; + end if; + end read_int; + +----------------------------------------------------------------------------- +-- *Module : sget_int +-- +-- *Description : Same as read_int except for strings +----------------------------------------------------------------------------- + procedure sget_int(S : in string; P : inout integer; X : out integer) is + variable char : character; + variable base : integer; + variable q : integer; + begin + if(S'length > P) then + char := ' '; + while(char = ' ') loop -- Skip spaces + char := S(P); + P := P + 1; + end loop; + base := 16; -- Hex is the default + if(char = '%') then -- determine base + char := S(P); + P := P + 1; + if(char = 'b' or char = 'B') then + base := 2; + elsif(char = 'x' or char = 'X') then + base := 16; + elsif(char = 'd' or char = 'D') then + base := 10; + else + assert false report "Unsupported Base detected when reading an integer" + severity error; + end if; + char := S(P); + P := P + 1; + end if; + q := 0; + if(is_hex(char)) then + q := q * base + hex_char_to_int(char); + while(is_hex(char) and not (S'length = P)) loop + char := S(P); + P := P + 1; + if(is_hex(char)) then + q := q * base + hex_char_to_int(char); + end if; + end loop; + end if; + X := q; + else + assert false report "Couldn't read an integer" + severity error; + end if; + end sget_int; + +----------------------------------------------------------------------------- +-- *Module : write_hex_vector +-- +-- *Description : writes out a vector as hex +----------------------------------------------------------------------------- + procedure write_hex_vector(L : inout line; V : in STD_ULOGIC_VECTOR) is + variable C : character; + variable VV : STD_ULOGIC_VECTOR(((V'length + 3)/4) * 4 - 1 downto 0); + begin + + VV := (others => '0'); + VV(V'length -1 downto 0) := V; + + for i in VV'length/4 - 1 downto 0 loop + C := vector_to_hex_char(VV(i*4+3 downto i*4)); + write(L, C); + end loop; + + end write_hex_vector; + +----------------------------------------------------------------------------- +-- *Module : to_str +-- +-- *Description : Converts a STD_ULOGIC_VECTOR to a string of the same length +----------------------------------------------------------------------------- + function to_str(constant V: in STD_ULOGIC_VECTOR) return STRING is + variable S : STRING(1 to V'length); + variable sp : integer; + begin + sp := 1; + for i in V'range loop + case V(i) is + when '1' | 'H' => + S(sp) := '1'; + when '0' | 'L' => + S(sp) := '0'; + when others => + S(sp) := 'X'; + end case; + sp := sp + 1; + end loop; + return(S); + end to_str; + +----------------------------------------------------------------------------- +-- *Module : to_str +-- +-- *Description : Converts a STD_ULOGIC to a string +----------------------------------------------------------------------------- + function to_str(constant V: in STD_ULOGIC) return STRING is +-- variable S : STRING(1); + begin + case V is + when '1' | 'H' => + return("1"); + when '0' | 'L' => + return("0"); + when others => + return("X"); + end case; + return("X"); + end to_str; + +----------------------------------------------------------------------------- +-- *Module : to_str +-- +-- *Description : Converts a integer to a string +----------------------------------------------------------------------------- + function to_str(constant val : in INTEGER) return STRING is + variable result : STRING(11 downto 1) := "-2147483648"; -- smallest integer and longest string + variable tmp : INTEGER; + variable pos : NATURAL := 1; + variable digit : NATURAL; + begin + -- for the smallest integer MOD does not seem to work... + --if val = -2147483648 then : compilation error with Xilinx tools... + if val < -2147483647 then + pos := 12; + else + tmp := abs(val); + loop + digit := abs(tmp MOD 10); + tmp := tmp / 10; + result(pos) := character'val(character'pos('0') + digit); + pos := pos + 1; + exit when tmp = 0; + end loop; + if val < 0 then + result(pos) := '-'; + pos := pos + 1; + end if; + end if; + return result((pos-1) downto 1); + end to_str; + +----------------------------------------------------------------------------- +-- *Module : to_strn +-- +-- *Description : Converts an integer to a string of length N +----------------------------------------------------------------------------- + function to_strn(constant val : in INTEGER; constant n : in INTEGER) return STRING is + variable result : STRING(11 downto 1) := "-2147483648"; -- smallest integer and longest string + variable tmp : INTEGER; + variable pos : NATURAL := 1; + variable digit : NATURAL; + begin + -- for the smallest integer MOD does not seem to work... + --if val = -2147483648 then : compilation error with Xilinx tools... + if val < -2147483647 then + pos := 12; + else + result := (others => ' '); + tmp := abs(val); + loop + digit := abs(tmp MOD 10); + tmp := tmp / 10; + result(pos) := character'val(character'pos('0') + digit); + pos := pos + 1; + exit when tmp = 0; + end loop; + if val < 0 then + result(pos) := '-'; + pos := pos + 1; + end if; + end if; + return result(n downto 1); + end to_strn; + + +end textutil; diff --git a/hdl/gn4124core/sim/gn4124_bfm/util.vhd b/hdl/gn4124core/sim/gn4124_bfm/util.vhd new file mode 100644 index 0000000000000000000000000000000000000000..c51777765340926dab50b075b00069b942f5e096 --- /dev/null +++ b/hdl/gn4124core/sim/gn4124_bfm/util.vhd @@ -0,0 +1,680 @@ +library ieee; +use ieee.std_logic_1164.all; +--library synopsys; +--use synopsys.arithmetic.all; + +package UTIL is + + type t_cmd_array is array (1 to 256) of integer; + + + function to_mvl ( b: in boolean ) return STD_ULOGIC; + function to_mvl ( i: in integer ) return STD_ULOGIC; + function to_vector(input,num_bits:integer) return STD_ULOGIC_VECTOR; +-- function to_signed( b: in std_ulogic_vector ) return signed; +-- function to_std_ulogic_vector( b: in signed ) return std_ulogic_vector; +-- function std_logic_to_std_ulogic( b: in std_logic ) return std_ulogic; +-- function std_ulogic_to_std_logic( b: in std_ulogic ) return std_logic; + function "and"(l: STD_ULOGIC_VECTOR; r: STD_ULOGIC) return STD_ULOGIC_VECTOR; + function "and"(l: STD_ULOGIC; r: STD_ULOGIC_VECTOR) return STD_ULOGIC_VECTOR; + function "and"(l: STD_ULOGIC_VECTOR; r: BOOLEAN) return STD_ULOGIC_VECTOR; + function "and"(l: BOOLEAN; r: STD_ULOGIC_VECTOR) return STD_ULOGIC_VECTOR; + function "and"(l: BOOLEAN; r: STD_ULOGIC) return STD_ULOGIC; + function "and"(l: STD_ULOGIC; r: BOOLEAN) return STD_ULOGIC; + function exp(input: STD_ULOGIC; num_bits: integer) return STD_ULOGIC_VECTOR; + function exp(input: STD_ULOGIC_VECTOR; num_bits: integer) return STD_ULOGIC_VECTOR; + function conv_integer ( ARG: in STD_ULOGIC_VECTOR ) return integer; + function "+"(l: STD_ULOGIC_VECTOR; r: STD_ULOGIC) return STD_ULOGIC_VECTOR; +-- function "+"(l: STD_ULOGIC_VECTOR; r: STD_ULOGIC_VECTOR) return STD_ULOGIC_VECTOR; +-- function "-"(l: STD_ULOGIC_VECTOR; r: STD_ULOGIC) return STD_ULOGIC_VECTOR; +-- function "-"(l: STD_ULOGIC_VECTOR; r: STD_ULOGIC_VECTOR) return STD_ULOGIC_VECTOR; + function to_int(l: std_ulogic_vector) return natural; + function to_int(l: std_ulogic) return natural; + function and_reduce(ARG: STD_ULOGIC_VECTOR) return STD_ULOGIC; + function nand_reduce(ARG: STD_ULOGIC_VECTOR) return STD_ULOGIC; + function or_reduce(ARG: STD_ULOGIC_VECTOR) return STD_ULOGIC; + function nor_reduce(ARG: STD_ULOGIC_VECTOR) return STD_ULOGIC; + function xor_reduce(ARG: STD_ULOGIC_VECTOR) return STD_ULOGIC; + function xnor_reduce(ARG: STD_ULOGIC_VECTOR) return STD_ULOGIC; + function ge ( l, r : STD_ULOGIC_VECTOR ) return BOOLEAN; + function gt ( l, r : STD_ULOGIC_VECTOR ) return BOOLEAN; + function lt ( l, r : STD_ULOGIC_VECTOR ) return BOOLEAN; + function eq ( l, r : STD_ULOGIC_VECTOR ) return BOOLEAN; + function maximum ( arg1, arg2 : INTEGER) return INTEGER; + function minimum ( arg1, arg2 : INTEGER) return INTEGER; + procedure keep(signal X: inout STD_LOGIC); + function log2(A: in integer) return integer; + ------------------------------------------------------------------- + -- Declaration of Synthesis directive attributes + ------------------------------------------------------------------- + ATTRIBUTE synthesis_return : string ; + +end UTIL; + +package body UTIL is + + -------------------------------------------------------------------- +-- function to_signed ( b: in std_ulogic_vector ) return signed is +-- variable result : signed(b'range); +-- begin +-- for i in b'range loop +-- result(i) := b(i); +-- end loop; +-- return result; +-- end to_signed; + + -------------------------------------------------------------------- +-- function to_std_ulogic_vector ( b: in signed ) return std_ulogic_vector is +-- variable result : std_ulogic_vector(b'range); +-- begin +-- for i in b'range loop +-- result(i) := b(i); +-- end loop; +-- return result; +-- end to_std_ulogic_vector; + + -------------------------------------------------------------------- + + function to_mvl ( b: in boolean ) return STD_ULOGIC is + begin + if ( b = TRUE ) then + return( '1' ); + else + return( '0' ); + end if; + end to_mvl; + + -------------------------------------------------------------------- + + function to_mvl ( i: in integer ) return STD_ULOGIC is + begin + if ( i = 1 ) then + return( '1' ); + else + return( '0' ); + end if; + end to_mvl; + + -------------------------------------------------------------------- + + function "and"(l: STD_ULOGIC; r: STD_ULOGIC_VECTOR) return STD_ULOGIC_VECTOR is + variable rr: STD_ULOGIC_vector(r'range); + begin + if (l = '1') then + rr := r; + else + rr := (others => '0'); + end if; + return(rr); + end; + + -------------------------------------------------------------------- + + function "and"(l: STD_ULOGIC_VECTOR; r: STD_ULOGIC) return STD_ULOGIC_VECTOR is + variable ll: STD_ULOGIC_vector(l'range); + begin + if (r = '1') then + ll := l; + else + ll := (others => '0'); + end if; + return(ll); + end; + + -------------------------------------------------------------------- + + function "and"(l: BOOLEAN; r: STD_ULOGIC_VECTOR) return STD_ULOGIC_VECTOR is + variable rr: STD_ULOGIC_vector(r'range); + begin + if (l) then + rr := r; + else + rr := (others => '0'); + end if; + return(rr); + end; + + -------------------------------------------------------------------- + + function "and"(l: STD_ULOGIC_VECTOR; r: BOOLEAN) return STD_ULOGIC_VECTOR is + variable ll: STD_ULOGIC_vector(l'range); + begin + if (r) then + ll := l; + else + ll := (others => '0'); + end if; + return(ll); + end; + + -------------------------------------------------------------------- + + function "and"(l: BOOLEAN; r: STD_ULOGIC) return STD_ULOGIC is + variable ll: STD_ULOGIC; + begin + if (l) then + ll := r; + else + ll := '0'; + end if; + return(ll); + end; + + -------------------------------------------------------------------- + + function "and"(l: STD_ULOGIC; r: BOOLEAN) return STD_ULOGIC is + variable ll: STD_ULOGIC; + begin + if (r) then + ll := l; + else + ll := '0'; + end if; + return(ll); + end; + + -------------------------------------------------------------------- + +-- function std_ulogic_to_std_logic(b : std_ulogic) return std_logic is +-- variable result: std_logic; +-- begin +-- result := b; +-- return result; +-- end; + + -------------------------------------------------------------------- + +-- function std_logic_to_std_ulogic(b : std_logic) return std_ulogic is +-- variable result: std_ulogic; +-- begin +-- result := b; +-- return result; +-- end; + + -------------------------------------------------------------------- + + function to_vector(input,num_bits: integer) return std_ulogic_vector is + variable vec: std_ulogic_vector(num_bits-1 downto 0); + variable a: integer; + begin + a := input; + for i in 0 to num_bits-1 loop + if ((a mod 2) = 1) then + vec(i) := '1'; + else + vec(i) := '0'; + end if; + a := a / 2; + end loop; + return vec; + end to_vector; + + + +-- FUNCTION to_vector(input,num_bits:integer) RETURN STD_ULOGIC_VECTOR IS +-- VARIABLE result:STD_ULOGIC_VECTOR(num_bits-1 DOWNTO 0); +-- VARIABLE weight:integer; +-- VARIABLE temp:integer; +-- BEGIN +-- weight := 2**(num_bits-1); +-- temp := input; +-- FOR i in result'HIGH DOWNTO result'LOW LOOP +-- IF temp >= weight THEN +-- result(i) := '1'; +-- temp := temp - weight; +-- ELSE +-- result(i) := '0'; +-- END IF; +-- weight := weight/2; +-- END LOOP; +-- RETURN result; +-- END to_vector; + + -------------------------------------------------------------------- + -- exp: Expand one bit into many + -------------------------------------------------------------------- + + FUNCTION exp(input:STD_ULOGIC; num_bits:integer) RETURN STD_ULOGIC_VECTOR IS + VARIABLE result:STD_ULOGIC_VECTOR(num_bits-1 DOWNTO 0); + BEGIN + FOR i in result'HIGH DOWNTO result'LOW LOOP + result(i) := input; + END LOOP; + RETURN result; + END exp; + + -------------------------------------------------------------------- + -- exp: Expand n bits into m bits + -------------------------------------------------------------------- + + FUNCTION exp(input:STD_ULOGIC_VECTOR; num_bits:integer) RETURN STD_ULOGIC_VECTOR IS + VARIABLE result:STD_ULOGIC_VECTOR(num_bits-1 DOWNTO 0); + BEGIN + result(input'high-input'low downto 0) := input; + result(num_bits-1 downto input'high-input'low+1) := (others => '0'); + RETURN result; + END exp; + + -------------------------------------------------------------------- + -- conv_integer + -------------------------------------------------------------------- + + function conv_integer ( ARG: in STD_ULOGIC_VECTOR ) return integer is + variable result: INTEGER; + begin + assert ARG'length <= 31 + report "ARG is too large in CONV_INTEGER" + severity FAILURE; + result := 0; + for i in ARG'range loop + result := result * 2; + if(ARG(i) = 'H' or ARG(i) = '1') then + result := result + 1; + end if; + end loop; + return result; + end; + + -------------------------------------------------------------------- + -- "+" Increment function + -------------------------------------------------------------------- + function "+"(L: STD_ULOGIC_VECTOR; R: STD_ULOGIC) return STD_ULOGIC_VECTOR is + variable Q: STD_ULOGIC_VECTOR(L'range); + variable A: STD_ULOGIC; + begin + A := R; + for i in L'low to L'high loop + Q(i) := L(i) xor A; + A := A and L(i); + end loop; + return Q; + end; + + -------------------------------------------------------------------- + -- "+" adder function + -------------------------------------------------------------------- +-- function "+"(L: STD_ULOGIC_VECTOR; R: STD_ULOGIC_VECTOR) return STD_ULOGIC_VECTOR is +-- variable Q : SIGNED(L'range); +-- variable result: STD_ULOGIC_VECTOR(L'range); +-- begin +-- Q := to_signed(L) + to_signed(R); +-- result := to_std_ulogic_vector(Q); +-- return result; +-- end; + + -------------------------------------------------------------------- + -- "-" Decrement function + -------------------------------------------------------------------- +-- function "-"(L: STD_ULOGIC_VECTOR; R: STD_ULOGIC) return STD_ULOGIC_VECTOR is +-- variable Q: STD_ULOGIC_VECTOR(L'range); +-- variable A: STD_ULOGIC; +-- begin +-- A := R; +-- for i in L'low to L'high loop +-- Q(i) := L(i) xor A; +-- A := A and not L(i); +-- end loop; +-- return Q; +-- end; + + -------------------------------------------------------------------- + -- "-" subtractor function + -------------------------------------------------------------------- +-- function "-"(L: STD_ULOGIC_VECTOR; R: STD_ULOGIC_VECTOR) return STD_ULOGIC_VECTOR is +-- variable Q : SIGNED(L'range); +-- variable result: STD_ULOGIC_VECTOR(L'range); +-- begin +-- Q := to_signed(L) - to_signed(R); +-- result := to_std_ulogic_vector(Q); +-- return result; +-- end; + + -------------------------------------------------------------------- + -- to_int : Convert std_ulogic_vector to an integer + -------------------------------------------------------------------- + function to_int(l: std_ulogic_vector) return natural is + variable result: natural := 0; + begin + for t1 in l'range loop + result := result * 2; + if (l(t1) = '1') or (l(t1) = 'H') then + result := result + 1; + end if; + end loop; + return result; + end to_int; + + -------------------------------------------------------------------- + -- to_int : Convert std_ulogic_vector to an integer + -------------------------------------------------------------------- + function to_int(l: std_ulogic) return natural is + variable result: natural := 0; + begin + if (l = '1') or (l = 'H') then + result := 1; + else + result := 0; + end if; + return result; + end to_int; + + -------------------------------------------------------------------- + -- Reduce Functions + -------------------------------------------------------------------- + function and_reduce(ARG: STD_ULOGIC_VECTOR) return STD_ULOGIC is + variable result: STD_ULOGIC; + begin + result := '1'; + for i in ARG'range loop + result := result and ARG(i); + end loop; + return result; + end; + + function nand_reduce(ARG: STD_ULOGIC_VECTOR) return STD_ULOGIC is + begin + return not and_reduce(ARG); + end; + + function or_reduce(ARG: STD_ULOGIC_VECTOR) return STD_ULOGIC is + variable result: STD_ULOGIC; + begin + result := '0'; + for i in ARG'range loop + result := result or ARG(i); + end loop; + return result; + end; + + function nor_reduce(ARG: STD_ULOGIC_VECTOR) return STD_ULOGIC is + begin + return not or_reduce(ARG); + end; + + function xor_reduce(ARG: STD_ULOGIC_VECTOR) return STD_ULOGIC is + variable result: STD_ULOGIC; + begin + result := '0'; + for i in ARG'range loop + result := result xor ARG(i); + end loop; + return result; + end; + + function xnor_reduce(ARG: STD_ULOGIC_VECTOR) return STD_ULOGIC is + begin + return not xor_reduce(ARG); + end; + + -------------------------------------------------------------------- + -- Some useful generic functions + -------------------------------------------------------------------- + --//// Zero Extend //// + -- + -- Function zxt + -- + FUNCTION zxt( q : STD_ULOGIC_VECTOR; i : INTEGER ) RETURN STD_ULOGIC_VECTOR IS + VARIABLE qs : STD_ULOGIC_VECTOR (1 TO i); + VARIABLE qt : STD_ULOGIC_VECTOR (1 TO q'length); + -- Hidden function. Synthesis directives are present in its callers + BEGIN + qt := q; + IF i < q'length THEN + qs := qt( (q'length-i+1) TO qt'right); + ELSIF i > q'length THEN + qs := (OTHERS=>'0'); + qs := qs(1 TO (i-q'length)) & qt; + ELSE + qs := qt; + END IF; + RETURN qs; + END; + + FUNCTION maximum (arg1,arg2:INTEGER) RETURN INTEGER IS + BEGIN + IF(arg1 > arg2) THEN + RETURN(arg1) ; + ELSE + RETURN(arg2) ; + END IF; + END ; + + FUNCTION minimum (arg1,arg2:INTEGER) RETURN INTEGER IS + BEGIN + IF(arg1 < arg2) THEN + RETURN(arg1) ; + ELSE + RETURN(arg2) ; + END IF; + END ; + + -------------------------------------------------------------------- + -- Comparision functions + -------------------------------------------------------------------- +-- +-- Equal functions. +-- + TYPE stdlogic_boolean_table IS ARRAY(std_ulogic, std_ulogic) OF BOOLEAN; + + CONSTANT eq_table : stdlogic_boolean_table := ( + -- + ---------------------------------------------------------------------------- + -- | U X 0 1 Z W L H D | | + -- + ---------------------------------------------------------------------------- + ( FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE ), -- | U | + ( FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE ), -- | X | + ( FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE ), -- | 0 | + ( FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE ), -- | 1 | + ( FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE ), -- | Z | + ( FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE ), -- | W | + ( FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE ), -- | L | + ( FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE ), -- | H | + ( FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE ) -- | D | + ); + + FUNCTION eq ( l, r : STD_LOGIC ) RETURN BOOLEAN IS + -- Equal for two logic types + VARIABLE result : BOOLEAN ; + ATTRIBUTE synthesis_return OF result:VARIABLE IS "EQ" ; + BEGIN + result := eq_table( l, r ); + RETURN result ; + END; + + FUNCTION eq ( l,r : STD_ULOGIC_VECTOR ) RETURN BOOLEAN IS + CONSTANT ml : INTEGER := maximum( l'length, r'length ); + VARIABLE lt : STD_ULOGIC_VECTOR ( 1 TO ml ); + VARIABLE rt : STD_ULOGIC_VECTOR ( 1 TO ml ); + -- Arithmetic Equal for two Unsigned vectors + VARIABLE result : BOOLEAN ; + ATTRIBUTE synthesis_return OF result:VARIABLE IS "EQ" ; + BEGIN + lt := zxt( l, ml ); + rt := zxt( r, ml ); + FOR i IN lt'range LOOP + IF NOT eq( lt(i), rt(i) ) THEN + result := FALSE; + RETURN result ; + END IF; + END LOOP; + RETURN TRUE; + END; + + TYPE std_ulogic_fuzzy_state IS ('U', 'X', 'T', 'F', 'N'); + TYPE std_ulogic_fuzzy_state_table IS ARRAY ( std_ulogic, std_ulogic ) OF std_ulogic_fuzzy_state; + + CONSTANT ge_fuzzy_table : std_ulogic_fuzzy_state_table := ( + -- ---------------------------------------------------- + -- | U X 0 1 Z W L H D | | + -- ---------------------------------------------------- + ( 'U', 'U', 'N', 'U', 'U', 'U', 'N', 'U', 'U' ), -- | U | + ( 'U', 'X', 'N', 'X', 'X', 'X', 'N', 'X', 'X' ), -- | X | + ( 'U', 'X', 'N', 'F', 'X', 'X', 'N', 'F', 'X' ), -- | 0 | + ( 'N', 'N', 'T', 'N', 'N', 'N', 'T', 'N', 'N' ), -- | 1 | + ( 'U', 'X', 'N', 'X', 'X', 'X', 'N', 'X', 'X' ), -- | Z | + ( 'U', 'X', 'N', 'X', 'X', 'X', 'N', 'X', 'X' ), -- | W | + ( 'U', 'X', 'N', 'F', 'X', 'X', 'N', 'F', 'X' ), -- | L | + ( 'N', 'N', 'T', 'N', 'N', 'N', 'T', 'N', 'N' ), -- | H | + ( 'U', 'X', 'N', 'X', 'X', 'X', 'N', 'X', 'X' ) -- | D | + ); + + FUNCTION ge ( L,R : std_ulogic_vector ) RETURN boolean IS + CONSTANT ml : integer := maximum( L'LENGTH, R'LENGTH ); + VARIABLE lt : std_ulogic_vector ( 1 to ml ); + VARIABLE rt : std_ulogic_vector ( 1 to ml ); + VARIABLE res : std_ulogic_fuzzy_state; + -- Greater-than-or-equal for two Unsigned vectors + VARIABLE result : BOOLEAN ; + ATTRIBUTE synthesis_return OF result:VARIABLE IS "GTE" ; + begin + lt := zxt( l, ml ); + rt := zxt( r, ml ); + FOR i IN lt'RANGE LOOP + res := ge_fuzzy_table( lt(i), rt(i) ); + CASE res IS + WHEN 'U' => RETURN FALSE; + WHEN 'X' => RETURN FALSE; + WHEN 'T' => RETURN TRUE; + WHEN 'F' => RETURN FALSE; + WHEN OTHERS => null; + END CASE; + END LOOP; + result := TRUE ; + RETURN result; + end ; + +-- +-- Greater Than functions. +-- + CONSTANT gtb_table : stdlogic_boolean_table := ( + -- + ---------------------------------------------------------------------------- + -- | U X 0 1 Z W L H D | | + -- + ---------------------------------------------------------------------------- + ( FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE ), -- | U | + ( FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE ), -- | X | + ( FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE ), -- | 0 | + ( FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE ), -- | 1 | + ( FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE ), -- | Z | + ( FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE ), -- | W | + ( FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE ), -- | L | + ( FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE ), -- | H | + ( FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE ) -- | D | + ); + + FUNCTION gt ( l, r : std_logic ) RETURN BOOLEAN IS + -- Greater-than for two logic types + VARIABLE result : BOOLEAN ; + ATTRIBUTE synthesis_return OF result:VARIABLE IS "GT" ; + BEGIN + result := gtb_table( l, r ); + RETURN result ; + END ; + + FUNCTION gt ( l,r : STD_ULOGIC_VECTOR ) RETURN BOOLEAN IS + CONSTANT ml : INTEGER := maximum( l'length, r'length ); + VARIABLE lt : STD_ULOGIC_VECTOR ( 1 TO ml ); + VARIABLE rt : STD_ULOGIC_VECTOR ( 1 TO ml ); + -- Greater-than for two logic unsigned vectors + VARIABLE result : BOOLEAN ; + ATTRIBUTE synthesis_return OF result:VARIABLE IS "GT" ; + BEGIN + lt := zxt( l, ml ); + rt := zxt( r, ml ); + FOR i IN lt'range LOOP + IF NOT eq( lt(i), rt(i) ) THEN + result := gt( lt(i), rt(i) ); + RETURN result ; + END IF; + END LOOP; + RETURN FALSE; + END; + +-- +-- Less Than functions. +-- + CONSTANT ltb_table : stdlogic_boolean_table := ( + -- + ---------------------------------------------------------------------------- + -- | U X 0 1 Z W L H D | | + -- + ---------------------------------------------------------------------------- + ( FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE ), -- | U | + ( FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE ), -- | X | + ( FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE ), -- | 0 | + ( FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE ), -- | 1 | + ( FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE ), -- | Z | + ( FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE ), -- | W | + ( FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE ), -- | L | + ( FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE ), -- | H | + ( FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE ) -- | D | + ); + + FUNCTION lt ( l, r : STD_LOGIC ) RETURN BOOLEAN IS + -- Less-than for two logic types + VARIABLE result : BOOLEAN ; + ATTRIBUTE synthesis_return OF result:VARIABLE IS "LT" ; + BEGIN + result := ltb_table( l, r ); + RETURN result ; + END; + + FUNCTION lt ( l,r : STD_ULOGIC_VECTOR ) RETURN BOOLEAN IS + CONSTANT ml : INTEGER := maximum( l'length, r'length ); + VARIABLE ltt : STD_ULOGIC_VECTOR ( 1 TO ml ); + VARIABLE rtt : STD_ULOGIC_VECTOR ( 1 TO ml ); + -- Less-than for two Unsigned vectors + VARIABLE result : BOOLEAN ; + ATTRIBUTE synthesis_return OF result:VARIABLE IS "LT" ; + BEGIN + ltt := zxt( l, ml ); + rtt := zxt( r, ml ); + FOR i IN ltt'range LOOP + IF NOT eq( ltt(i), rtt(i) ) THEN + result := lt( ltt(i), rtt(i) ); + RETURN result ; + END IF; + END LOOP; + RETURN FALSE; + END; + + -------------------------------------------------------------------- + -- "keep" Retain Last value when floated + -------------------------------------------------------------------- + procedure keep(signal X: inout STD_LOGIC) is + begin + if(X = 'Z') then + if(X'last_value = '0') then + X <= 'L'; + elsif(X'last_value = '1') then + X <= 'H'; + else + X <= 'Z'; + end if; + else + X <= 'Z'; + end if; + end keep; + + --------------------------------------------------------------------- + -- log base 2 function + --------------------------------------------------------------------- + function log2 ( A: in integer ) return integer is + variable B : integer; + begin + B := 1; + for i in 0 to 31 loop + if not ( A > B ) then + return ( i ); + exit; + end if; + B := B * 2; + end loop; + end log2; + + + +end UTIL; +