Commit ba4bbfd2 authored by Dimitris Lampridis's avatar Dimitris Lampridis

Merge branch 'proposed_master'

parents 97bc6910 074d4468
......@@ -4,6 +4,7 @@ modules = {
"modules/common",
"modules/genrams",
"modules/wishbone",
"modules/radtol",
"platform"
]
}
......@@ -44,6 +44,13 @@ In [modules/common](modules/common) there are general purpose cores:
contains a complex handling for asynchronous signals (crossing clock
domains, deglitcher, edge detection, pulse extension...)
* CDC modules come also with specific timing contraints in [modules/common/xdc](modules/common/xdc).
These constraints can be used in Vivado projects (so-called "module-bound" constraints)
to automatically derive proper timing constraints for CDC paths in each module.
To use it, add specific constraint file to your project and set `SCOPED_TO_REF`
property in GUI or your TCL file.
(e.g. add `gc_sync.xdc` if you use `gc_sync.vhd` and set `SCOPED_TO_REF=gc_sync`)
* For reset generation, you can use [gc_reset](modules/common/gc_reset.vhd)
which generate synchronous resets once all the PLL lock signals are set.
The module [gc_reset_multi_aasd](modules/common/gc_reset_multi_aasd.vhd)
......@@ -202,7 +209,10 @@ Directory [modules/wishbone](modules/wishbone) contains modules for wishbone.
* There are utilities to handle a wishbone bus:
- [wb_clock_crossing](modules/wishbone/wb_clock_crossing) handle clock domain
crossing.
- [wb_register](modules/wishbone/wb_register) add a pipeline register.
- [wb_register](modules/wishbone/wb_register) adds a pipeline register.
- [wb_skidpad2](modules/wishbone/wb_register) adds a pipeline register to
a pipelined wishbone bus (in one direction only) without downgrading
the throughput.
* There are modules to convert to a different bus
- [wb_async_bridge](modules/wishbone/wb_async_bridge) is a bridge with the
......
......@@ -2,6 +2,7 @@ modules = { "local" : [
"z7_axi_gpio_expander",
"axi4lite_wb_bridge",
"axi4lite32_axi4full64_bridge",
"axi4lite_axi4full_bridge",
]}
files = [
......
files = [
"axi4lite_axi4full_bridge.vhd",
];
......@@ -42,5 +42,4 @@ files = [
"gc_async_counter_diff.vhd",
"gc_sync_word_wr.vhd",
"gc_sync_word_rd.vhd",
"gc_simple_spi_master.vhd"
];
......@@ -89,31 +89,38 @@ begin -- rtl
p_input_ack : process(clk_in_i)
begin
if rising_edge(clk_in_i) then
d_p_d0 <= d_p_i;
d_ack_d0 <= d_ack;
if ready = '1' and d_p_i = '1' and d_p_d0 = '0'then
-- Incoming pulse detected and the system is ready.
-- Transfer it.
in_ext <= '1';
-- Clear ack and ready!
d_ack <= '0';
ready <= '0';
elsif in_ext = '1' and out_feedback = '1' then
-- Pulse has been transfered, clear the input.
in_ext <= '0';
elsif in_ext = '0' and out_feedback = '0' then
-- Clear transfered. Done.
-- This is also the steady state.
d_ack <= '1';
if rst_in_n_i = '0' then
d_p_d0 <= '0';
d_ack <= '0';
d_ack_d0 <= '0';
ready <= '1';
end if;
if ready = '0' then
assert d_p_i = '0' or (d_p_i = '1' and d_p_d0 = '1')
report "request while previous one not completed"
severity ERROR;
in_ext <= '0';
else
d_p_d0 <= d_p_i;
d_ack_d0 <= d_ack;
if ready = '1' and d_p_i = '1' and d_p_d0 = '0'then
-- Incoming pulse detected and the system is ready.
-- Transfer it.
in_ext <= '1';
-- Clear ack and ready!
d_ack <= '0';
ready <= '0';
elsif in_ext = '1' and out_feedback = '1' then
-- Pulse has been transfered, clear the input.
in_ext <= '0';
elsif in_ext = '0' and out_feedback = '0' then
-- Clear transfered. Done.
-- This is also the steady state.
d_ack <= '1';
ready <= '1';
end if;
if ready = '0' then
assert d_p_i = '0' or (d_p_i = '1' and d_p_d0 = '1')
report "request while previous one not completed"
severity ERROR;
end if;
end if;
end if;
end process p_input_ack;
......
......@@ -67,6 +67,7 @@ architecture arch of gc_sync_word_rd is
signal d_ready : std_logic;
signal wr_in : std_logic;
signal rd_out : std_logic;
begin
cmp_pulse_sync : entity work.gc_pulse_synchronizer2
port map (
......@@ -88,18 +89,20 @@ begin
end if;
end process;
p_writer : process (clk_out_i)
p_writer : process(clk_out_i)
begin
if rising_edge(clk_out_i) then
if rst_in_n_i = '0' then
ack_out_o <= '0';
elsif wr_in = '1' then
if wr_in = '1' then
-- Data is stable.
data_out_o <= gc_sync_word_data;
ack_out_o <= '1';
ack_out_o <= '1';
else
ack_out_o <= '0';
end if;
if rst_out_n_i = '0' then
ack_out_o <= '0';
end if;
end if;
end process;
end arch;
# the "-quiet" option is added for the use case where this module is added to
# the project, but not instatiated (e.g. because of generic settings)
# in that case Vivado would throw critical warnings during P&$
set_false_path -quiet -to [get_pins -hierarchical *rst_chains_reg[*]/CLR]
# Timing constrains for basic 1 bit synchroniser.
# In many cases this could be solved with simple set_false_path, but in some
# cases this module is used in more time-critical applications, eg. inferred FIFO.
# In that case it makes sense to apply some max_delay constraint.
#
# You can always override any of these max_delay constraints in your global XDC
# with set_false_path because it has the highest priority.
set clk [get_clocks -of_objects [get_ports clk_i]]
set clk_period [get_property PERIOD $clk]
# ATTENTION: we can't use "all_fanin" to find the source register because
# apparently this command doesn't traverse outside of scoped reference (even with -flat switch)
# This method won't work properly if there's a combinational path between a source and target FF;
# but in a proper CDC circuit it's forbidded to have logic between FFs anyway!
set dst_ff [get_pins sync_*.sync0_*/D]
set src_ff [get_cells -of_objects [get_pins -filter {IS_LEAF && DIRECTION == OUT} -of_objects [get_nets -segments -of_objects $dst_ff]]]
# We use -quiet switch, because otherwise Vivado will throw critical warning
# if module is not used in the project (e.g. due to generics)
set_max_delay $clk_period -quiet -datapath_only -from $src_ff -to $dst_ff
# Timing constrains for basic bit vector synchroniser.
#
# This is similar to gc_sync, but vector synchronisation is usually more tricky.
# Usually you really want to limit bus skew and delay to one clock cycle.
#
# You can always override any of these max_delay constraints in your global XDC
# with set_false_path because it has the highest priority.
set clk [get_clocks -of_objects [get_ports clk_i]]
set clk_period [get_property PERIOD $clk]
# ATTENTION: we can't use "all_fanin" to find the source register because
# apparently this command doesn't traverse outside of scoped reference (even with -flat switch)
# This method won't work properly if there's a combinational path between a source and target FF;
# but in a proper CDC circuit it's forbidded to have logic between FFs anyway!
set dst_ff [get_pins sync0_*[*]/D]
set src_ff [get_cells -of_objects [get_pins -filter {IS_LEAF && DIRECTION == OUT} -of_objects [get_nets -segments -of_objects $dst_ff]]]
# We use -quiet switch, because otherwise Vivado will throw critical warning
# if module is not used in the project (e.g. due to generics)
set_max_delay $clk_period -quiet -datapath_only -from $src_ff -to $dst_ff
set_bus_skew $clk_period -quiet -from $src_ff -to $dst_ff
# Timing constrains for read word synchronizer.
# Flag handskaking is done by pulse synchroniser submodule which should have its
# own constraint file and thus isn't covered here
set src_clk [get_clocks -of_objects [get_ports clk_in_i]]
set dst_clk [get_clocks -of_objects [get_ports clk_out_i]]
set src_clk_period [get_property PERIOD $src_clk]
set dst_clk_period [get_property PERIOD $dst_clk]
set skew_value [expr {(($src_clk_period < $dst_clk_period) ? $src_clk_period : $dst_clk_period)}]
set src_ff [get_pins gc_sync_word_data*[*]/C]
set dst_ff [get_pins data_out*[*]/D]
# We use -quiet switch, because otherwise Vivado will throw critical warning
# if module is not used in the project (e.g. due to generics)
set_max_delay $skew_value -quiet -datapath_only -from $src_ff -to $dst_ff
set_bus_skew $skew_value -quiet -from $src_ff -to $dst_ff
# Timing constrains for write word synchronizer.
# Flag handskaking is done by pulse synchroniser submodule which should have its
# own constraint file and thus isn't covered here
set src_clk [get_clocks -of_objects [get_ports clk_in_i]]
set dst_clk [get_clocks -of_objects [get_ports clk_out_i]]
set src_clk_period [get_property PERIOD $src_clk]
set dst_clk_period [get_property PERIOD $dst_clk]
set skew_value [expr {(($src_clk_period < $dst_clk_period) ? $src_clk_period : $dst_clk_period)}]
set src_ff [get_pins gc_sync_word_data*[*]/C]
set dst_ff [get_pins dat_out*[*]/D]
# We use -quiet switch, because otherwise Vivado will throw critical warning
# if module is not used in the project (e.g. due to generics)
set_max_delay $skew_value -quiet -datapath_only -from $src_ff -to $dst_ff
set_bus_skew $skew_value -quiet -from $src_ff -to $dst_ff
......@@ -88,7 +88,7 @@ package body memory_loader_pkg is
open_status : in file_open_status;
fail_if_notfound : in boolean) is
begin
if open_status /= OPEN_OK then
if not (open_status = OPEN_OK) then
if fail_if_notfound then
report "f_load_mem_from_file(): can't open file '"&file_name&"'" severity FAILURE;
......
files = [
"secded_32b_pkg.vhd",
"voter_status.vhd",
"voter_vec_status.vhd",
]
--------------------------------------------------------------------------------
-- CERN BE-CO-HT
-- General Cores Library
-- https://www.ohwr.org/projects/general-cores
--------------------------------------------------------------------------------
--
-- unit name: secded_32b_pkg
--
-- description: ECC on 32b
--
--------------------------------------------------------------------------------
-- Copyright CERN 2020-2021
--------------------------------------------------------------------------------
-- Copyright and related rights are licensed under the Solderpad Hardware
-- License, Version 2.0 (the "License"); you may not use this file except
-- in compliance with the License. You may obtain a copy of the License at
-- http://solderpad.org/licenses/SHL-2.0.
-- Unless required by applicable law or agreed to in writing, software,
-- hardware and materials distributed under this License is distributed on an
-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
-- or implied. See the License for the specific language governing permissions
-- and limitations under the License.
--------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
package secded_32b_pkg is
subtype data_word_t is std_logic_vector (31 downto 0);
subtype ecc_word_t is std_logic_vector (6 downto 0);
-- Compute the ECC bits for DATA.
-- The ECC is a xor of some DATA bits.
function f_calc_ecc (data : data_word_t) return ecc_word_t;
-- SYNDROME is the xor of read ECC and recomputed ECC.
-- The xor should be 0, except in case of errors.
-- Return '1' if there is a difference (so if SYNDROME is not 0)
function f_ecc_errors (syndrome : ecc_word_t) return std_logic;
-- Return '1' if the number of SYNDOME bits set to 1 is odd.
-- (a one bit error results in 1 or 3 bits set in the syndrome)
function f_ecc_one_error (syndrome : ecc_word_t) return std_logic;
-- Fix the error (if any).
-- Returns new ecc + data, from syndrome and original ecc + data.
function f_fix_error (syndrome : ecc_word_t;
ecc : ecc_word_t;
data : data_word_t) return std_logic_vector;
end secded_32b_pkg;
package body secded_32b_pkg is
function f_xor (x : std_logic_vector) return std_logic is
variable result : std_logic := '0';
begin
for i in x'range loop
result := result xor x(i);
end loop;
return result;
end f_xor;
type syndrome_mask_array is array(0 to 6) of data_word_t;
constant syndrome_masks : syndrome_mask_array := (
0 => "11000001010010000100000011111111",
1 => "00100001001001001111111110010000",
2 => "01101100111111110000100000001000",
3 => "11111111000000011010010001000100",
4 => "00010110111100001001001010100110",
5 => "00010000000111110111000101100001",
6 => "10001010100000100000111100011011"
);
function f_calc_ecc (data : data_word_t) return ecc_word_t
is
variable result : ecc_word_t;
begin
for i in result'range loop
result (i) := f_xor (data and syndrome_masks (i));
end loop;
return result;
end f_calc_ecc;
function f_ecc_errors (syndrome : ecc_word_t) return std_logic
is
variable result : std_logic := '0';
begin
-- There is at least one error if the syndrome is not 0.
for i in syndrome'range loop
result := result or syndrome(i);
end loop;
return result;
end f_ecc_errors;
function f_ecc_one_error (syndrome : ecc_word_t) return std_logic is
begin
-- If there is no error, syndrome is 0 so it will return 0.
-- If there is one error, 1 or 3 bits are set in the syndrome, so returns 1.
-- If there are 2 errors, 2, 4 or 6 bits are set in the syncrome, so returns 0.
-- If there are more than 2 errors, all bets are off (it's a secded).
return f_xor (syndrome);
end f_ecc_one_error;
function f_fix_error (syndrome : ecc_word_t;
ecc : ecc_word_t;
data : data_word_t) return std_logic_vector
is
variable result : data_word_t := (others => '1');
begin
-- Compute which data bits have been altered.
-- If a data bit is altered, its corresponding ECC bits are altered too.
-- So, conversely (and because there is only one error), the altered ECC bits
-- designate the altered data bit.
for i in 0 to 31 loop
for k in 0 to 6 loop
if syndrome_masks(k)(i) = '1' then
result(i) := result(i) and syndrome(k);
end if;
end loop;
end loop;
-- Return the fixed ecc+data.
if result /= (data_word_t'range => '0') then
return ecc & (data xor result);
else
return (syndrome xor ecc) & (data xor result);
end if;
end f_fix_error;
end secded_32b_pkg;
--------------------------------------------------------------------------------
-- CERN BE-CO-HT
-- General Cores Library
-- https://www.ohwr.org/projects/general-cores
--------------------------------------------------------------------------------
--
-- unit name: secded_ecc
--
-- description: SECDED RAM controller
--
--------------------------------------------------------------------------------
-- Copyright CERN 2020-2021
--------------------------------------------------------------------------------
-- Copyright and related rights are licensed under the Solderpad Hardware
-- License, Version 2.0 (the "License"); you may not use this file except
-- in compliance with the License. You may obtain a copy of the License at
-- http://solderpad.org/licenses/SHL-2.0.
-- Unless required by applicable law or agreed to in writing, software,
-- hardware and materials distributed under this License is distributed on an
-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
-- or implied. See the License for the specific language governing permissions
-- and limitations under the License.
--------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use work.secded_32b_pkg.all;
entity secded_ecc is
generic (
g_addr_width : natural := 16
);
port (
clk_i : in std_logic;
rst_i : in std_logic;
-- to the processor/bus
a_i : in std_logic_vector(g_addr_width-1 downto 0);
d_i : in std_logic_vector(31 downto 0);
we_i : in std_logic;
bwe_i : in std_logic_vector (3 downto 0);
re_i : in std_logic;
q_o : out std_logic_vector(31 downto 0);
done_r_o : out std_logic;
done_w_o : out std_logic;
--to the BRAM
a_ram_o : out std_logic_vector (g_addr_width-1 downto 0);
d_ram_i : in std_logic_vector (38 downto 0);
q_ram_o : out std_logic_vector (38 downto 0);
we_ram_o : out std_logic;
re_ram_o : out std_logic;
valid_ram_i : in std_logic;
lock_req_o : out std_logic;
lock_grant_i : in std_logic;
single_error_p_o : out std_logic;
double_error_p_o : out std_logic
);
end secded_ecc;
architecture rtl of secded_ecc is
function f_mask_word (mask : std_logic_vector (3 downto 0);
d1 : std_logic_vector (31 downto 0);
d2 : std_logic_vector (31 downto 0)) return std_logic_vector
is
variable masked_word : std_logic_vector (31 downto 0);
begin
for i in 1 to 4 loop
if mask(i-1) = '0' then
masked_word (i*8-1 downto (i-1)*8) := d2(i*8-1 downto (i-1)*8);
else
masked_word (i*8-1 downto (i-1)*8) := d1(i*8-1 downto (i-1)*8);
end if;
end loop;
return masked_word;
end f_mask_word;
type fsm_read_states is (normal_op, check_again, wait_lock, wait_correction);
type fsm_write_states is (normal_op, check_write);
type fsm_rw_states is (idle, wait_lock, wait_read, wait_write);
signal fsm_read : fsm_read_states := normal_op;
signal fsm_write : fsm_write_states := normal_op;
signal fsm_rw : fsm_rw_states := idle;
signal ecc_errors, ecc_correctable_error : std_logic;
signal syndrome : std_logic_vector (6 downto 0);
signal re_d, re_fsm, we_fsm, re, we, valid_ram_d : std_logic;
signal done_r, done_w, fsm_done_r_p, fsm_done_rw_p : std_logic;
signal lock_req_r, lock_req_rw : std_logic;
signal req_correction, ack_correction : std_logic;
signal fsm_read_normal : std_logic;
signal d, d_rw : std_logic_vector (31 downto 0);
signal q_ram : std_logic_vector (38 downto 0);
attribute syn_radhardlevel : string;
attribute syn_radhardlevel of rtl : architecture is "tmr";
begin
q_o <= d_ram_i (31 downto 0);
re_ram_o <= '1' when (fsm_read /= normal_op) else re;
syndrome <= f_calc_ecc (d_ram_i (31 downto 0)) xor d_ram_i(38 downto 32);
ecc_errors <= f_ecc_errors(syndrome);
ecc_correctable_error <= f_ecc_one_error (syndrome);
fsm_read_normal <= '1' when fsm_read = normal_op else '0';
done_r <= (re_d and valid_ram_d and fsm_read_normal and not ecc_errors) or fsm_done_r_p;
done_r_o <= done_r when (fsm_rw = idle) else '0';
done_w_o <= '1' when (fsm_done_rw_p = '1' or (done_w = '1' and (fsm_rw = idle))) else '0';
process (rst_i, a_i, q_ram)
begin
-- synthesis translate_off
if rst_i = '1' then
a_ram_o <= (others => '0');
q_ram_o <= (others => '0');
else
-- synthesis translate_on
a_ram_o <= a_i;
q_ram_o <= q_ram;
-- synthesis translate_off
end if;
-- synthesis translate_on
end process;
lock_req_o <= lock_req_r or lock_req_rw;
we <= we_i when (bwe_i = "1111") else we_fsm;
re <= re_i or re_fsm;
d <= d_i when (fsm_rw = idle) else d_rw;
-- this FSM is used for sub-word writing, which require a word read and a word write
-- the read and write is atomic, dual-port thread-safe
process (clk_i)
begin
if rising_edge (clk_i) then
if rst_i = '1' then
fsm_rw <= idle;
lock_req_rw <= '0';
re_fsm <= '0';
we_fsm <= '0';
fsm_done_rw_p <= '0';
else
case fsm_rw is
when idle =>
fsm_done_rw_p <= '0';
if we_i = '1' and bwe_i /= "1111" then
lock_req_rw <= '1';
fsm_rw <= wait_lock;
end if;
when wait_lock =>
if lock_grant_i = '1' then
re_fsm <= '1';
fsm_rw <= wait_read;
end if;
when wait_read =>
re_fsm <= '0';
if done_r = '1' then
d_rw <= f_mask_word (bwe_i, d_i, d_ram_i (31 downto 0));
fsm_rw <= wait_write;
we_fsm <= '1';
end if;
when wait_write =>
we_fsm <= '0';
if done_w = '1' then
fsm_done_rw_p <= '1';
lock_req_rw <= '0';
fsm_rw <= idle;
end if;
when others =>
fsm_rw <= idle;
end case;
end if;
end if;
end process;
process (clk_i)
begin
if rising_edge(clk_i) then
if rst_i = '1' then
fsm_read <= normal_op;
re_d <= '0';
fsm_done_r_p <= '0';
lock_req_r <= '0';
req_correction <= '0';
single_error_p_o <= '0';
double_error_p_o <= '0';
else
re_d <= re;
valid_ram_d <= valid_ram_i;
case fsm_read is
when normal_op =>
fsm_done_r_p <= '0';
single_error_p_o <= '0';
double_error_p_o <= '0';
if re_d = '1' and done_r = '0' and valid_ram_d = '1' and ecc_errors = '1' then
fsm_read <= check_again; -- SET?
end if;
when check_again =>
if valid_ram_d = '1' and ecc_errors = '1' then
if ecc_correctable_error = '0' then
double_error_p_o <= '1';
fsm_done_r_p <= '1';
fsm_read <= normal_op;
else
lock_req_r <= '1';
fsm_read <= wait_lock;
end if;
elsif valid_ram_d = '1' and ecc_errors = '0' then
fsm_read <= normal_op;
end if;
when wait_lock =>
if lock_grant_i = '1' then
req_correction <= '1';
fsm_read <= wait_correction;
end if;
when wait_correction =>
--req_correction <= '0';
if ack_correction = '1' then
req_correction <= '0';
fsm_read <= normal_op;
fsm_done_r_p <= '1';
single_error_p_o <= '1';
lock_req_r <= '0';
end if;
end case;
end if;
end if;
end process;
process (clk_i)
begin
if rising_edge (clk_i) then
if rst_i = '1' then
fsm_write <= normal_op;
done_w <= '0';
we_ram_o <= '0';
else
ack_correction <= '0';
done_w <= '0';
case fsm_write is
when normal_op =>
ack_correction <= '0';
done_w <= '0';
if req_correction = '1' and ack_correction = '0' then
q_ram <= f_fix_error (syndrome, d_ram_i(38 downto 32), d_ram_i (31 downto 0));
we_ram_o <= '1';
fsm_write <= check_write;
elsif we = '1' then
q_ram(31 downto 0) <= d;
q_ram(38 downto 32) <= f_calc_ecc(d);
we_ram_o <= '1';
fsm_write <= check_write;
end if;
when check_write =>
if valid_ram_i = '1' then
we_ram_o <= '0';
fsm_write <= normal_op;
if req_correction = '0' then
done_w <= '1';
else
ack_correction <= '1';
end if;
end if;
end case;
end if;
end if;
end process;
end architecture;
--------------------------------------------------------------------------------
-- CERN BE-CO-HT
-- General Cores Library
-- https://www.ohwr.org/projects/general-cores
--------------------------------------------------------------------------------
--
-- unit name: voter_status
--
-- description: 3 input majority voter with error status output
--
--------------------------------------------------------------------------------
-- Copyright CERN 2022
--------------------------------------------------------------------------------
-- Copyright and related rights are licensed under the Solderpad Hardware
-- License, Version 2.0 (the "License"); you may not use this file except
-- in compliance with the License. You may obtain a copy of the License at
-- http://solderpad.org/licenses/SHL-2.0.
-- Unless required by applicable law or agreed to in writing, software,
-- hardware and materials distributed under this License is distributed on an
-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
-- or implied. See the License for the specific language governing permissions
-- and limitations under the License.
--------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
entity voter_status is
port (
inp : in std_logic_vector(1 to 3);
res : out std_logic;
err : out std_logic);
end voter_status;
architecture behav of voter_status is
begin
res <= (inp(1) and inp(2)) or (inp(1) and inp(3)) or (inp(2) and inp(3));
err <= (not (inp(1) and inp(2) and inp(3))) and (not (not inp(1) and not inp(2) and not inp(3)));
end behav;
\ No newline at end of file
--------------------------------------------------------------------------------
-- CERN BE-CO-HT
-- General Cores Library
-- https://www.ohwr.org/projects/general-cores
--------------------------------------------------------------------------------
--
-- unit name: voter_vec_status
--
-- description: 3 input majority voter with error status output for a vector
-- NOTE: in case of error, the result may be different from all the inputs
-- (if two errors appear)
--
--------------------------------------------------------------------------------
-- Copyright CERN 2022
--------------------------------------------------------------------------------
-- Copyright and related rights are licensed under the Solderpad Hardware
-- License, Version 2.0 (the "License"); you may not use this file except
-- in compliance with the License. You may obtain a copy of the License at
-- http://solderpad.org/licenses/SHL-2.0.
-- Unless required by applicable law or agreed to in writing, software,
-- hardware and materials distributed under this License is distributed on an
-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
-- or implied. See the License for the specific language governing permissions
-- and limitations under the License.
--------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
entity voter_vec_status is
generic (
g_WIDTH : natural
);
port (
a, b, c : in std_logic_vector(g_WIDTH - 1 downto 0);
res : out std_logic_vector(g_WIDTH - 1 downto 0);
err : out std_logic);
end voter_vec_status;
architecture behav of voter_vec_status is
signal b_err : std_logic_vector(g_WIDTH - 1 downto 0);
begin
gen_bit: for i in res'range generate
inst_voter: entity work.voter_status
port map (
inp (1) => a(i),
inp (2) => b(i),
inp (3) => c(i),
res => res(i),
err => b_err(i)
);
end generate;
err <= '1' when b_err /= (b_err'range => '0') else '0';
end behav;
\ No newline at end of file
......@@ -109,7 +109,7 @@ begin
if (wb_master_i.stall = '0') then
wb_master_o.stb <= '0';
if (wb_master_i.ack = '1') then
state <= IDLE;
state <= RESPONSE_READ;
axi4_slave_o.RRESP <= c_AXI4_RESP_OKAY;
axi4_slave_o.RDATA <= wb_master_i.dat;
axi4_slave_o.RVALID <= '1';
......
files = [
"fine_pulse_gen_kintex7_shared.vhd",
"fine_pulse_gen_kintexultrascale_shared.vhd",
"fine_pulse_gen_kintex7.vhd",
"fine_pulse_gen_kintexultrascale.vhd",
"fine_pulse_gen_wbgen2_pkg.vhd",
"fine_pulse_gen_wb.vhd",
"xwb_fine_pulse_gen.vhd"]
import logging
if target == "xilinx":
files = [
"fine_pulse_gen_kintex7_shared.vhd",
"fine_pulse_gen_kintexultrascale_shared.vhd",
"fine_pulse_gen_kintex7.vhd",
"fine_pulse_gen_kintexultrascale.vhd",
"fine_pulse_gen_wbgen2_pkg.vhd",
"fine_pulse_gen_wb.vhd",
"xwb_fine_pulse_gen.vhd"]
else:
logging.info("Library component wb_fine_pulse_gen targets only xilinx devices")
......@@ -165,7 +165,7 @@ begin
port map (
RDY => odelayctrl_rdy_o,
REFCLK => clk_odelay,
RST => odelayctrl_rst_i
RST => rst_synced
);
end block;
end generate gen_use_odelay;
......
......@@ -2,4 +2,5 @@ files = [
"xwb_register_link.vhd",
"wb_skidpad.vhd",
"xwb_register.vhd",
"wb_skidpad2.vhd",
]
--------------------------------------------------------------------------------
-- CERN BE-CO-HT
-- General Cores Library
-- https://www.ohwr.org/projects/general-cores
--------------------------------------------------------------------------------
--
-- unit name: xwb_skidpad2
--
-- Add registers in a wishbone flow to help timing closure while maintaining
-- full throughput. WB PIPELINE ONLY.
--
-- Differences with other modules in this directory:
-- * wb_skidpad: wishbone interface, any data width
-- * xwb_register: any data/addr width, full throughput, pipeline only.
-- * xwb_register_link: any data/addr width, pipeline only.
-- Also, note that CYC is not handled (either connect to 1, to stb or to your
-- own logic).
--
--------------------------------------------------------------------------------
-- Copyright CERN 2014-2018
--------------------------------------------------------------------------------
-- Copyright and related rights are licensed under the Solderpad Hardware
-- License, Version 2.0 (the "License"); you may not use this file except
-- in compliance with the License. You may obtain a copy of the License at
-- http://solderpad.org/licenses/SHL-2.0.
-- Unless required by applicable law or agreed to in writing, software,
-- hardware and materials distributed under this License is distributed on an
-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
-- or implied. See the License for the specific language governing permissions
-- and limitations under the License.
--------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity wb_skidpad2 is
generic (
-- Number of bits in adr.
g_adrbits : natural := 32;
-- Number of bits in dat.
g_datbits : natural := 32
);
port (
clk_i : std_logic;
rst_n_i : std_logic;
-- The slave port (note: no dat_o, no ack).
stb_i : in std_logic;
adr_i : in std_logic_vector(g_adrbits-1 downto 0);
dat_i : in std_logic_vector(g_datbits-1 downto 0);
sel_i : in std_logic_vector((g_datbits/8)-1 downto 0);
we_i : in std_logic;
stall_o : out std_logic;
-- The master port (note: no dat_i, no ack).
stb_o : out std_logic;
adr_o : out std_logic_vector(g_adrbits-1 downto 0);
dat_o : out std_logic_vector(g_datbits-1 downto 0);
sel_o : out std_logic_vector((g_datbits/8)-1 downto 0);
we_o : out std_logic;
stall_i : in std_logic
);
end wb_skidpad2;
architecture rtl of wb_skidpad2 is
signal r_full0, r_full1 : std_logic := '0';
signal fill0, fill1 : std_logic;
signal stall : std_logic;
signal r_adr0, r_adr1 : std_logic_vector (g_adrbits - 1 downto 0);
signal r_dat0, r_dat1 : std_logic_vector (g_datbits - 1 downto 0);
signal r_sel0, r_sel1 : std_logic_vector ((g_datbits / 8) - 1 downto 0);
signal r_we0, r_we1 : std_logic;
begin
-- A tfr is possible if one of the position is full.
stb_o <= r_full1 or r_full0;
-- The skidpad is full if both positions are full. Do not check for stall_i as it will create
-- a combinational path.
stall <= r_full1 and r_full0;
stall_o <= stall;
-- Write into position 0 iff: input valid skidpad not stalling.
fill0 <= stb_i and not stall;
-- Move from position 0 to 1 when possible:
-- Position 0 is valid, and position 1 is empty and not read or full and read.
fill1 <= r_full0 and (r_full1 xor stall_i);
control : process(clk_i, rst_n_i) is
begin
if rst_n_i = '0' then
r_full0 <= '0';
r_full1 <= '0';
elsif rising_edge(clk_i) then
r_full0 <= fill0 or (r_full1 and stall_i);
r_full1 <= fill1 or (r_full1 and stall_i);
end if;
end process;
bulk : process(clk_i) is
begin
if rising_edge(clk_i) then
if fill0 = '1' then
r_dat0 <= dat_i;
r_adr0 <= adr_i;
r_sel0 <= sel_i;
r_we0 <= we_i;
end if;
if fill1 = '1' then
r_dat1 <= r_dat0;
r_adr1 <= r_adr0;
r_sel1 <= r_sel0;
r_we1 <= r_we0;
end if;
end if;
end process;
adr_o <= r_adr1 when r_full1 = '1' else r_adr0;
dat_o <= r_dat1 when r_full1 = '1' else r_dat0;
sel_o <= r_sel1 when r_full1 = '1' else r_sel0;
we_o <= r_we1 when r_full1 = '1' else r_we0;
end rtl;
......@@ -79,7 +79,7 @@ architecture behaviour of wb_tics is
signal cntr_overflow : std_logic;
begin
process(clk_sys_i)
begin
if rising_edge(clk_sys_i) then
......@@ -112,7 +112,7 @@ begin
--Wishbone interface
wb_dat_o <= std_logic_vector(cntr_tics);
wb_ack_o <= '1';
wb_ack_o <= wb_stb_i and wb_cyc_i;
wb_stall_o <= '0';
--process(clk_sys_i)
......
memory-map:
name: spi
description: Wishbone SPI
bus: wb-32-be
x-hdl:
busgroup: True
x-wbgen:
hdl_entity: spi_wishbone_slave
schema-version:
core: 2.0.0
x-conversions: 1.0.0
x-driver-edge: 1.0.0
x-enums: 1.0.0
x-fesa: 2.0.0
x-gena: 2.0.0
x-hdl: 1.0.0
x-map-info: 1.0.0
x-wbgen: 1.0.0
children:
- reg:
name: tx_rx_0
description: TX/RX 0
comment:
width: 32
access: rw
address: 0x0
x-wbgen:
access_bus: READ_WRITE
access_dev: READ_WRITE
field_description: Tx/Rx word 0
load: LOAD_EXT
type: SLV
x-hdl:
write-strobe: True
- reg:
name: tx_rx_1
description: TX/RX 1
comment:
width: 32
access: rw
address: 0x4
x-wbgen:
access_bus: READ_WRITE
access_dev: READ_WRITE
field_description: Tx/Rx word 1
load: LOAD_EXT
type: SLV
x-hdl:
write-strobe: True
- reg:
name: tx_rx_2
description: TX/RX 2
comment:
width: 32
access: rw
address: 0x8
x-wbgen:
access_bus: READ_WRITE
access_dev: READ_WRITE
field_description: Tx/Rx word 2
load: LOAD_EXT
type: SLV
x-hdl:
write-strobe: True
- reg:
name: tx_rx_3
description: TX/RX 3
comment:
width: 32
access: rw
address: 0xc
x-wbgen:
access_bus: READ_WRITE
access_dev: READ_WRITE
field_description: Tx/Rx word 3
load: LOAD_EXT
type: SLV
x-hdl:
write-strobe: True
- reg:
name: ctrl
description: Control register
width: 32
access: rw
address: 0x10
children:
- field:
name: len
description: Length of SPI transfer
range: 6-0
x-wbgen:
access_bus: READ_WRITE
access_dev: READ_ONLY
type: SLV
- field:
name: go
description: Start SPI transfer
range: 8
x-wbgen:
access_bus: WRITE_ONLY
access_dev: READ_ONLY
size: 1
type: MONOSTABLE
- field:
name: rx_negedge
description: RX negedge
comment: 'write 1: data from Slave received on falling SCLK edge \n write 0: data from Slave received on rising SCLK edge'
range: 9
x-wbgen:
access_bus: READ_WRITE
access_dev: READ_ONLY
size: 1
type: BIT
- field:
name: tx_negedge
description: TX negedge
comment: 'write 1: data transmitted on falling SCLK edge \n write 0: data transmitted on rising SCLK edge'
range: 10
x-wbgen:
access_bus: READ_WRITE
access_dev: READ_ONLY
size: 1
type: BIT
- field:
name: lsb
description: LSB first
comment: 'write 1: LSB first on the line \n write 0: MSB first on the line'
range: 11
x-wbgen:
access_bus: READ_WRITE
access_dev: READ_ONLY
size: 1
type: BIT
- field:
name: irq
description: Interrupt enable
comment: 'write 1: IRQ enabled \n write 0: IRQ disabled'
range: 12
x-wbgen:
access_bus: READ_WRITE
access_dev: READ_ONLY
size: 1
type: BIT
- field:
name: ass
description: Automatic Slave select
range: 13
x-wbgen:
access_bus: READ_WRITE
access_dev: READ_ONLY
size: 1
type: BIT
- reg:
name: divider
description: Divider
comment:
width: 32
access: rw
address: 0x14
children:
- field:
name: value
description: Divide factor
range: 15-0
x-wbgen:
access_bus: READ_WRITE
access_dev: READ_ONLY
field_description: Divide factor
type: SLV
- reg:
name: ss
description: Select SPI slave
comment:
width: 32
access: rw
address: 0x18
x-hdl:
write-strobe: True
children:
- field:
name: value
description: Select slave
range: 0
x-wbgen:
access_bus: READ_WRITE
access_dev: READ_WRITE
field_description: Select slave
load: LOAD_EXT
size: 1
type: SLV
files = ["wb_xc7_fw_update_regs.vhd",
"xwb_xc7_fw_update.vhd",
]
import logging
if target == "xilinx":
files = ["wb_xc7_fw_update_regs.vhd",
"xwb_xc7_fw_update.vhd",
"xwb_xc7_fw_update_v2.vhd",
]
else:
logging.info("Library component wb_xc7_fw_update targets only xilinx devices")
-------------------------------------------------------------------------------
-- Title : XC7 firmware update
-- Project : General Cores
-------------------------------------------------------------------------------
-- Note: The spi clock is directly connected to the STARTUPE2 module, so it
-- doesn't appear as an output.
-------------------------------------------------------------------------------
-- Copyright (c) 2020-2021 CERN
--
-- Copyright and related rights are licensed under the Solderpad Hardware
-- License, Version 0.51 (the "License") (which enables you, at your option,
-- to treat this file as licensed under the Apache License 2.0); you may not
-- use this file except in compliance with the License. You may obtain a copy
-- of the License at http://solderpad.org/licenses/SHL-0.51.
-- Unless required by applicable law or agreed to in writing, software,
-- hardware and materials distributed under this License is distributed on an
-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
-- or implied. See the License for the specific language governing permissions
-- and limitations under the License.
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
......@@ -21,71 +42,18 @@ entity xwb_xc7_fw_update is
end xwb_xc7_fw_update;
architecture rtl of xwb_xc7_fw_update is
signal far_data_in : std_logic_vector(7 downto 0);
signal far_data_out : std_logic_vector(7 downto 0);
signal far_xfer_out : std_logic;
signal far_ready_in : std_logic;
signal far_cs_out : std_logic;
signal far_wr_out : std_logic;
signal flash_spi_cs : std_logic;
signal flash_spi_start : std_logic;
signal flash_spi_wdata : std_logic_vector(7 downto 0);
signal flash_sclk : std_logic;
begin
inst_regs: entity work.wb_xc7_fw_update_regs
i_inst: entity work.xwb_xc7_fw_update_v2
port map (
clk_i => clk_i,
clk_i => clk_i,
rst_n_i => rst_n_i,
wb_i => wb_i,
wb_o => wb_o,
far_data_i => far_data_in,
far_data_o => far_data_out,
far_xfer_i => '0',
far_xfer_o => far_xfer_out,
far_ready_i => far_ready_in,
far_ready_o => open,
far_cs_i => '0',
far_cs_o => far_cs_out,
far_wr_o => far_wr_out
);
-- Need to capture cs and data_out, and need to delay start.
p_host_spi_registers : process(clk_i)
begin
if rising_edge(clk_i) then
if rst_n_i = '0' then
flash_spi_start <= '0';
flash_spi_wdata <= (others => '0');
flash_spi_cs <= '0';
elsif far_wr_out = '1' then
flash_spi_wdata <= far_data_out;
flash_spi_start <= far_xfer_out;
flash_spi_cs <= far_cs_out;
else
-- Pulse for start.
flash_spi_start <= '0';
end if;
end if;
end process;
U_SPI_Master : entity work.gc_simple_spi_master
generic map (
g_div_ratio_log2 => 0,
g_num_data_bits => 8)
port map (
clk_sys_i => clk_i,
rst_n_i => rst_n_i,
cs_i => flash_spi_cs,
start_i => flash_spi_start,
cpol_i => '0',
data_i => flash_spi_wdata,
ready_o => far_ready_in,
data_o => far_data_in,
spi_cs_n_o => flash_cs_n_o,
spi_sclk_o => flash_sclk,
spi_mosi_o => flash_mosi_o,
spi_miso_i => flash_miso_i);
wb_i => wb_i,
wb_o => wb_o,
flash_cs_n_o => flash_cs_n_o,
flash_mosi_o => flash_mosi_o,
flash_miso_i => flash_miso_i,
flash_sck_o => flash_sclk);
STARTUPE2_inst : STARTUPE2
generic map (
......
-------------------------------------------------------------------------------
-- Title : XC7 firmware update
-- Project : General Cores
-------------------------------------------------------------------------------
-- Note: Contrary to V1, this version doesn't include the STARTUPE2 module
-- so the spi clock port is added.
-------------------------------------------------------------------------------
-- Copyright (c) 2020-2021 CERN
--
-- Copyright and related rights are licensed under the Solderpad Hardware
-- License, Version 0.51 (the "License") (which enables you, at your option,
-- to treat this file as licensed under the Apache License 2.0); you may not
-- use this file except in compliance with the License. You may obtain a copy
-- of the License at http://solderpad.org/licenses/SHL-0.51.
-- Unless required by applicable law or agreed to in writing, software,
-- hardware and materials distributed under this License is distributed on an
-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
-- or implied. See the License for the specific language governing permissions
-- and limitations under the License.
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use work.wishbone_pkg.all;
entity xwb_xc7_fw_update_v2 is
port (
clk_i : in std_logic;
rst_n_i : in std_logic;
wb_i : in t_wishbone_slave_in;
wb_o : out t_wishbone_slave_out;
flash_cs_n_o : out std_logic;
flash_mosi_o : out std_logic;
flash_miso_i : in std_logic;
flash_sck_o : out std_logic
);
end xwb_xc7_fw_update_v2;
architecture rtl of xwb_xc7_fw_update_v2 is
signal far_data_in : std_logic_vector(7 downto 0);
signal far_data_out : std_logic_vector(7 downto 0);
signal far_xfer_out : std_logic;
signal far_ready_in : std_logic;
signal far_cs_out : std_logic;
signal far_wr_out : std_logic;
signal flash_spi_cs : std_logic;
signal flash_spi_start : std_logic;
signal flash_spi_wdata : std_logic_vector(7 downto 0);
signal flash_sclk : std_logic;
begin
inst_regs: entity work.wb_xc7_fw_update_regs
port map (
clk_i => clk_i,
rst_n_i => rst_n_i,
wb_i => wb_i,
wb_o => wb_o,
far_data_i => far_data_in,
far_data_o => far_data_out,
far_xfer_i => '0',
far_xfer_o => far_xfer_out,
far_ready_i => far_ready_in,
far_ready_o => open,
far_cs_i => '0',
far_cs_o => far_cs_out,
far_wr_o => far_wr_out
);
-- Need to capture cs and data_out, and need to delay start.
p_host_spi_registers : process(clk_i)
begin
if rising_edge(clk_i) then
if rst_n_i = '0' then
flash_spi_start <= '0';
flash_spi_wdata <= (others => '0');
flash_spi_cs <= '0';
elsif far_wr_out = '1' then
flash_spi_wdata <= far_data_out;
flash_spi_start <= far_xfer_out;
flash_spi_cs <= far_cs_out;
else
-- Pulse for start.
flash_spi_start <= '0';
end if;
end if;
end process;
U_SPI_Master : entity work.gc_simple_spi_master
generic map (
g_div_ratio_log2 => 0,
g_num_data_bits => 8)
port map (
clk_sys_i => clk_i,
rst_n_i => rst_n_i,
cs_i => flash_spi_cs,
start_i => flash_spi_start,
cpol_i => '0',
data_i => flash_spi_wdata,
ready_o => far_ready_in,
data_o => far_data_in,
spi_cs_n_o => flash_cs_n_o,
spi_sclk_o => flash_sclk,
spi_mosi_o => flash_mosi_o,
spi_miso_i => flash_miso_i);
flash_sck_o <= flash_sclk;
end rtl;
......@@ -14,19 +14,19 @@ entity pcie_wb is
port(
clk125_i : in std_logic; -- 125 MHz, free running
cal_clk50_i : in std_logic; -- 50 MHz, shared between all PHYs
-- Physical PCIe pins
pcie_refclk_i : in std_logic; -- 100 MHz, must not derive clk125_i or cal_clk50_i
pcie_rstn_i : in std_logic; -- Asynchronous "clear sticky" PCIe pin
pcie_rx_i : in std_logic_vector(3 downto 0);
pcie_tx_o : out std_logic_vector(3 downto 0);
-- Commands from PC to FPGA
master_clk_i : in std_logic;
master_rstn_i : in std_logic;
master_o : out t_wishbone_master_out;
master_i : in t_wishbone_master_in;
-- Command to PC from FPGA
slave_clk_i : in std_logic := '0';
slave_rstn_i : in std_logic := '1';
......@@ -35,30 +35,30 @@ entity pcie_wb is
end pcie_wb;
architecture rtl of pcie_wb is
signal internal_wb_clk, internal_wb_rstn, stall : std_logic;
signal internal_wb_clk, internal_wb_rstn, stall : std_logic;
signal internal_wb_rstn_sync : std_logic_vector(3 downto 0) := (others => '0');
signal rx_wb64_stb, rx_wb64_stall : std_logic;
signal rx_wb32_stb, rx_wb32_stall : std_logic;
signal rx_wb64_dat : std_logic_vector(63 downto 0);
signal rx_wb32_dat : std_logic_vector(31 downto 0);
signal rx_bar : std_logic_vector(2 downto 0);
signal tx_rdy, tx_eop : std_logic;
signal tx64_alloc, tx_wb64_stb : std_logic;
signal tx32_alloc, tx_wb32_stb : std_logic;
signal tx_wb64_dat : std_logic_vector(63 downto 0);
signal tx_wb32_dat : std_logic_vector(31 downto 0);
signal tx_alloc_mask : std_logic := '1'; -- Only pass every even tx32_alloc to tx64_alloc.
signal wb_stb, wb_ack, wb_stall : std_logic;
signal wb_adr : std_logic_vector(63 downto 0);
signal wb_bar : std_logic_vector(2 downto 0);
signal wb_dat : std_logic_vector(31 downto 0);
signal cfg_busdev : std_logic_vector(12 downto 0);
-- Internal WB clock, PC->FPGA
signal int_slave_i : t_wishbone_slave_in;
signal int_slave_o : t_wishbone_slave_out;
......@@ -66,25 +66,25 @@ architecture rtl of pcie_wb is
signal int_master_o : t_wishbone_master_out;
signal int_master_i : t_wishbone_master_in;
signal ext_slave_o : t_wishbone_slave_out;
-- control registers
signal r_cyc : std_logic;
signal r_int : std_logic := '0'; -- interrupt mask, starts=0
signal r_addr : std_logic_vector(31 downto 16);
signal r_error : std_logic_vector(63 downto 0);
-- interrupt signals
signal fifo_full, r_fifo_full, app_int_sts, app_msi_req : std_logic;
begin
pcie_phy : pcie_altera
pcie_phy : pcie_altera
generic map(
g_family => g_family)
port map(
clk125_i => clk125_i,
cal_clk50_i => cal_clk50_i,
async_rstn => master_rstn_i and slave_rstn_i,
pcie_refclk_i => pcie_refclk_i,
pcie_rstn_i => pcie_rstn_i,
pcie_rx_i => pcie_rx_i,
......@@ -96,19 +96,19 @@ begin
wb_clk_o => internal_wb_clk,
wb_rstn_i => internal_wb_rstn,
rx_wb_stb_o => rx_wb64_stb,
rx_wb_dat_o => rx_wb64_dat,
rx_wb_stall_i => rx_wb64_stall,
rx_bar_o => rx_bar,
tx_rdy_o => tx_rdy,
tx_alloc_i => tx64_alloc,
tx_wb_stb_i => tx_wb64_stb,
tx_wb_dat_i => tx_wb64_dat,
tx_eop_i => tx_eop);
pcie_rx : pcie_64to32 port map(
clk_i => internal_wb_clk,
rstn_i => internal_wb_rstn,
......@@ -118,7 +118,7 @@ begin
slave32_stb_o => rx_wb32_stb,
slave32_dat_o => rx_wb32_dat,
slave32_stall_i => rx_wb32_stall);
pcie_tx : pcie_32to64 port map(
clk_i => internal_wb_clk,
rstn_i => internal_wb_rstn,
......@@ -128,24 +128,24 @@ begin
slave64_stb_o => tx_wb64_stb,
slave64_dat_o => tx_wb64_dat,
slave64_stall_i => '0');
pcie_logic : pcie_tlp port map(
clk_i => internal_wb_clk,
rstn_i => internal_wb_rstn,
rx_wb_stb_i => rx_wb32_stb,
rx_wb_dat_i => rx_wb32_dat,
rx_wb_stall_o => rx_wb32_stall,
rx_bar_i => rx_bar,
tx_rdy_i => tx_rdy,
tx_alloc_o => tx32_alloc,
tx_wb_stb_o => tx_wb32_stb,
tx_wb_dat_o => tx_wb32_dat,
tx_eop_o => tx_eop,
cfg_busdev_i => cfg_busdev,
wb_stb_o => wb_stb,
wb_adr_o => wb_adr,
wb_bar_o => wb_bar,
......@@ -157,14 +157,14 @@ begin
wb_err_i => int_slave_o.err,
wb_rty_i => int_slave_o.rty,
wb_dat_i => wb_dat);
internal_wb_rstn <= internal_wb_rstn_sync(0);
tx64_alloc <= tx32_alloc and tx_alloc_mask;
alloc : process(internal_wb_clk)
begin
if rising_edge(internal_wb_clk) then
internal_wb_rstn_sync <= (master_rstn_i and slave_rstn_i) & internal_wb_rstn_sync(internal_wb_rstn_sync'length-1 downto 1);
if internal_wb_rstn = '0' then
tx_alloc_mask <= '1';
else
......@@ -172,43 +172,43 @@ begin
end if;
end if;
end process;
PC_to_FPGA_clock_crossing : xwb_clock_crossing
PC_to_FPGA_clock_crossing : xwb_clock_crossing
generic map(g_size => 32) port map(
slave_clk_i => internal_wb_clk,
slave_rst_n_i => internal_wb_rstn,
slave_i => int_slave_i,
slave_o => int_slave_o,
master_clk_i => master_clk_i,
master_clk_i => master_clk_i,
master_rst_n_i => master_rstn_i,
master_i => master_i,
master_o => master_o);
int_slave_i.stb <= wb_stb when wb_bar = "001" else '0';
wb_stall <= int_slave_o.stall when wb_bar = "001" else '0';
int_slave_i.cyc <= r_cyc;
int_slave_i.adr(r_addr'range) <= r_addr;
int_slave_i.adr(r_addr'right-1 downto 0) <= wb_adr(r_addr'right-1 downto 0);
FPGA_to_PC_clock_crossing : xwb_clock_crossing
generic map(g_size => 32) port map(
slave_clk_i => slave_clk_i,
slave_rst_n_i => slave_rstn_i,
slave_i => slave_i,
slave_o => ext_slave_o,
master_clk_i => internal_wb_clk,
master_clk_i => internal_wb_clk,
master_rst_n_i => internal_wb_rstn,
master_i => int_master_i,
master_o => int_master_o);
-- Do not wait for software acknowledgement
fask_ack : if g_fast_ack generate
slave_o.stall <= ext_slave_o.stall;
slave_o.rty <= '0';
slave_o.err <= '0';
slave_o.dat <= (others => '0');
fast_ack : process(slave_clk_i)
begin
if rising_edge(slave_clk_i) then
......@@ -216,7 +216,7 @@ begin
end if;
end process;
end generate;
-- Uses ack/err and dat from software
slow_ack : if not g_fast_ack generate
slave_o <= ext_slave_o;
......@@ -226,26 +226,26 @@ begin
fifo_full <= int_master_o.cyc and int_master_o.stb;
app_int_sts <= fifo_full and r_int; -- Classic interrupt until FIFO drained
app_msi_req <= fifo_full and not r_fifo_full; -- Edge-triggered MSI
int_master_i.rty <= '0';
control : process(internal_wb_clk)
begin
if rising_edge(internal_wb_clk) then
r_fifo_full <= fifo_full;
-- Shift in the error register
if int_slave_o.ack = '1' or int_slave_o.err = '1' or int_slave_o.rty = '1' then
r_error <= r_error(r_error'length-2 downto 0) & (int_slave_o.err or int_slave_o.rty);
end if;
if wb_bar = "001" then
wb_ack <= int_slave_o.ack;
wb_dat <= int_slave_o.dat;
else -- The control BAR is targetted
-- Feedback acks one cycle after strobe
wb_ack <= wb_stb;
-- Always output read result (even w/o stb or we)
case wb_adr(6 downto 2) is
when "00000" => -- Control register high
......@@ -274,12 +274,12 @@ begin
when others =>
wb_dat <= (others => '0');
end case;
-- Unless requested to by the PC, don't deque the FPGA->PC FIFO
int_master_i.stall <= '1';
int_master_i.ack <= '0';
int_master_i.err <= '0';
-- Is this a write to the register space?
if wb_stb = '1' and int_slave_i.we = '1' then
case wb_adr(6 downto 2) is
......@@ -306,6 +306,7 @@ begin
when "01" => int_master_i.stall <= '0';
when "10" => int_master_i.ack <= '1';
when "11" => int_master_i.err <= '1';
when others => null;
end case;
end if;
when "10101" => -- Master FIFO data low
......@@ -316,5 +317,5 @@ begin
end if;
end if;
end process;
end rtl;
......@@ -72,8 +72,8 @@ interface IWishboneMaster
WAIT_ACK
} xf_state;
wb_cycle_t request_queue[$];
wb_cycle_t result_queue[$];
mailbox #(wb_cycle_t) request_queue;
mailbox #(wb_cycle_t) result_queue;
struct {
int gen_random_throttling;
......@@ -152,7 +152,7 @@ interface IWishboneMaster
dat_o <= gen_data(c.data[i]);
@(posedge clk_i);
while (ack != 1'b1 && err != 1'b1 && rty == 1'b1) @(posedge clk_i);
while (ack != 1'b1 && err != 1'b1 && rty == 1'b0) @(posedge clk_i);
if (err || rty) begin
c.result = (err ? R_ERROR: R_RETRY);
......@@ -283,35 +283,29 @@ class CIWBMasterAccessor extends CWishboneAccessor;
endfunction // poll
task get(ref wb_cycle_t xfer);
while(!result_queue.size())
@(posedge clk_i);
xfer = result_queue.pop_front();
result_queue.get(xfer);
endtask // get
task put(ref wb_cycle_t xfer);
request_queue.push_back(xfer);
request_queue.put(xfer);
endtask // put
function int idle();
return (request_queue.size() == 0) && (xf_state == IDLE);
return (request_queue.num() == 0) && (xf_state == IDLE);
endfunction // idle
endclass // CIWBMasterAccessor
CIWBMasterAccessor theAccessor;
initial
theAccessor = new;
function automatic CIWBMasterAccessor get_accessor();
return theAccessor;
endfunction // get_accessor
always@(posedge clk_i)
if (!rst_n_i) begin
request_queue = {};
result_queue = {};
request_queue = new();
result_queue = new();
xf_state = IDLE;
cyc <= 0;
dat_o <= 0;
......@@ -322,30 +316,38 @@ endclass // CIWBMasterAccessor
end
initial begin
theAccessor = new;
request_queue = new();
result_queue = new();
settings.gen_random_throttling = 0;
settings.throttle_prob = 0.1;
settings.addr_gran = WORD;
end
initial forever begin
@(posedge clk_i);
if (request_queue.size() > 0) begin
forever begin
wb_cycle_t c;
c = request_queue.pop_front();
@(posedge clk_i);
case(c.ctype)
PIPELINED:
pipelined_cycle(c);
CLASSIC:
classic_cycle(c);
endcase
if (request_queue.try_get(c)) begin
result_queue.push_back(c);
end
case (c.ctype)
PIPELINED:
pipelined_cycle(c);
CLASSIC:
classic_cycle(c);
endcase
// Notify any waiting thread that the transfer is complete
->c.done;
result_queue.put(c);
end // if (request_queue.try_get(c))
end // forever begin
end
end // initial begin
endinterface // IWishboneMaster
......@@ -9,16 +9,12 @@ virtual class CWishboneAccessor extends CBusAccessor;
protected wb_cycle_type_t m_cycle_type;
function new();
$display("NEW");
m_cycle_type = CLASSIC;
m_default_xfer_size = 4;
endfunction // new
virtual task automatic set_mode(wb_cycle_type_t mode);
m_cycle_type = mode;
$display("SET MODE %d", mode );
endtask // set_mode
......@@ -70,8 +66,11 @@ virtual class CWishboneAccessor extends CBusAccessor;
end
// $display("DS: %d", cycle.data.size());
put(cycle);
// wait for the transfer completion notification to avoid getting the result of the wrong transfer
// in case multiple threads are calling readm()/writem() in parallel.
@cycle.done;
get(cycle);
result = cycle.result;
......@@ -83,7 +82,6 @@ virtual class CWishboneAccessor extends CBusAccessor;
int i;
cycle.ctype = m_cycle_type;
$display("CYCLE CTYPE %d %d", cycle.ctype, m_cycle_type );
cycle.rw = 1'b0;
......@@ -96,6 +94,9 @@ virtual class CWishboneAccessor extends CBusAccessor;
end
put(cycle);
// wait for the transfer completion notification to avoid getting the result of the wrong transfer
// in case multiple threads are calling readm()/writem() in parallel.
@cycle.done;
get(cycle);
for(i=0;i < addr.size(); i++)
......
......@@ -42,6 +42,7 @@ typedef struct {
wb_cycle_type_t ctype;
wb_xfer_t data[$];
wb_cycle_result_t result;
event done;
} wb_cycle_t;
typedef enum
......
//------------------------------------------------------------------------------
// CERN BE-CEM-EDL
// General Cores Library
// https://www.ohwr.org/projects/general-cores
//------------------------------------------------------------------------------
//
// unit name: CSimDrv_WB_SPI
//
// author: Grzegorz Daniluk
//
// description: SV wb_spi master driver for testbenches
//
//------------------------------------------------------------------------------
// Copyright CERN 2021
//------------------------------------------------------------------------------
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 2.0 (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-2.0.
// Unless required by applicable law or agreed to in writing, software,
// hardware and materials distributed under this License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
// or implied. See the License for the specific language governing permissions
// and limitations under the License.
//------------------------------------------------------------------------------
`ifndef __SIMDRV_WB_SPI_SVH
`define __SIMDRV_WB_SPI_SVH 1
`define SPI_REG_RX0 0
`define SPI_REG_TX0 0
`define SPI_REG_RX1 4
`define SPI_REG_TX1 4
`define SPI_REG_RX2 8
`define SPI_REG_TX2 8
`define SPI_REG_RX3 12
`define SPI_REG_TX3 12
`define SPI_REG_CTRL 16
`define SPI_REG_DIVIDER 20
`define SPI_REG_SS 24
`define SPI_CTRL_ASS (1<<13) // automatic slave select (nCS)
`define SPI_CTRL_IE (1<<12) // interrupt enable
`define SPI_CTRL_LSB (1<<11) // LSB first on line
`define SPI_CTRL_TXNEG (1<<10) // MOSI driven on negative SCLK edge
`define SPI_CTRL_RXNEG (1<<9) // MISO sampled on negative SCLK edge
`define SPI_CTRL_GO_BSY (1<<8)
`define SPI_CTRL_CHAR_LEN(x) ((x) & 'h7f)
class CSimDrv_WB_SPI;
protected CBusAccessor acc;
protected uint64_t base;
function new(CBusAccessor busacc, uint64_t adr);
acc = busacc;
base = adr;
endfunction
task init();
// set divider
acc.write(base + `SPI_REG_DIVIDER, 10);
endtask;
task cs(int state);
acc.write(base + `SPI_REG_SS, state);
endtask
task txrx(uint32_t in, int nbits, output uint32_t out);
uint64_t rval;
// configure transfer
acc.write(`SPI_REG_CTRL, `SPI_CTRL_CHAR_LEN(nbits) | `SPI_CTRL_TXNEG);
acc.write(`SPI_REG_TX0, in);
// start transfer
acc.write(`SPI_REG_CTRL, `SPI_CTRL_CHAR_LEN(nbits) | `SPI_CTRL_TXNEG | `SPI_CTRL_GO_BSY);
do begin
acc.read(`SPI_REG_CTRL, rval);
end while (rval & `SPI_CTRL_GO_BSY);
acc.read(`SPI_REG_RX0, rval);
out = rval;
endtask
endclass
`endif
library ieee;
use ieee.std_logic_1164.all;
use work.secded_32b_pkg.all;
entity tb_secded_32b_pkg is
end tb_secded_32b_pkg;
architecture behav of tb_secded_32b_pkg is
function image(v : std_logic_vector) return string
is
alias va : std_logic_vector(1 to v'length) is v;
variable res : string (va'range);
begin
for i in va'range loop
if va (i) = '1' then
res (i) := '1';
else
res (i) := '0';
end if;
end loop;
return res;
end image;
signal orig_data, data : std_logic_vector (31 downto 0);
signal orig_ecc, ecc, comp_ecc, syndrome : std_logic_vector(6 downto 0);
signal err, cor : std_logic_vector(38 downto 0);
type vectors_type is
array (natural range <>) of std_logic_vector(31 downto 0);
constant vectors : vectors_type :=
(x"00001197",
x"22c18193",
x"00040117",
x"ffffffff");
begin
process
variable ecc2 : std_logic_vector(6 downto 0);
begin
for i in vectors'range loop
ecc2 := f_calc_ecc (vectors (i));
report "data: " & to_hstring (vectors (i)) & ", ecc: " & to_hstring (ecc2);
end loop;
orig_data <= x"789a_d3f5";
syndrome <= "0000000";
wait for 1 ns;
assert f_ecc_errors (syndrome) = '0' severity failure;
assert f_ecc_one_error (syndrome) = '0' severity failure;
orig_ecc <= f_calc_ecc (orig_data);
-- Single error (detection and correction)
for i in 0 to 38 loop
err <= (others => '0');
err (i) <= '1';
wait for 1 ns;
if i < 32 then
-- Bit flip in data
data <= orig_data xor err(31 downto 0);
ecc <= orig_ecc;
else
-- Bit flip in ecc
ecc <= orig_ecc xor err(38 downto 32);
data <= orig_data;
end if;
wait for 1 ns;
comp_ecc <= f_calc_ecc (data);
wait for 1 ns;
syndrome <= comp_ecc xor ecc;
wait for 1 ns;
assert f_ecc_errors (syndrome) = '1' severity failure;
assert f_ecc_one_error (syndrome) = '1' severity failure;
cor <= f_fix_error(syndrome, ecc, data);
wait for 1 ns;
report "data: " & image (data) & ", ecc: " & image (ecc) & ", err: " & image (err) & ", ecc/err: " & image(comp_ecc);
report "cdata: " & image (cor(31 downto 0)) & ", cecc: " & image(cor(38 downto 32)) & " syndrome: " & image(syndrome);
assert cor(31 downto 0) = orig_data severity failure;
assert cor(38 downto 32) = orig_ecc severity failure;
end loop;
report "end of test";
wait;
end process;
end behav;
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity tb_secded_ecc is
end tb_secded_ecc;
architecture arch of tb_secded_ecc is
constant addr_width : natural := 8;
subtype ram_word_t is std_logic_vector (38 downto 0);
signal clk : std_logic;
signal rst : std_logic;
-- to the processor/bus
signal addr : std_logic_vector(addr_width-1 downto 0);
signal din : std_logic_vector(31 downto 0);
signal we : std_logic;
signal bwe : std_logic_vector (3 downto 0);
signal re : std_logic;
signal dout : std_logic_vector(31 downto 0);
signal done_r : std_logic;
signal done_w : std_logic;
--to the BRAM
signal a_ram : std_logic_vector (addr_width-1 downto 0);
signal d_ram : ram_word_t;
signal q_ram : ram_word_t;
signal we_ram : std_logic;
signal re_ram : std_logic;
signal valid_ram : std_logic;
signal lock_req : std_logic;
signal lock_grant : std_logic;
signal force_err : ram_word_t := (others => '0');
signal single_error_p : std_logic;
signal double_error_p : std_logic;
signal end_of_test : boolean := false;
begin
process
begin
clk <= '0';
wait for 5 ns;
clk <= '1';
wait for 5 ns;
if end_of_test then
wait;
end if;
end process;
rst <= '1', '0' after 20 ns;
process (clk)
type mem_t is array (2**addr_width - 1 downto 0) of ram_word_t;
variable mem : mem_t;
begin
if rising_edge(clk) then
valid_ram <= '0';
if we_ram = '1' then
mem (to_integer(unsigned(a_ram))) := q_ram xor force_err;
valid_ram <= '1';
end if;
if re_ram = '1' then
d_ram <= mem (to_integer(unsigned(a_ram)));
valid_ram <= '1';
end if;
end if;
end process;
inst_secded: entity work.secded_ecc
generic map (
g_addr_width => addr_width
)
port map (
clk_i => clk,
rst_i => rst,
a_i => addr,
d_i => din,
we_i => we,
bwe_i => bwe,
re_i => re,
q_o => dout,
done_r_o => done_r,
done_w_o => done_w,
a_ram_o => a_ram,
d_ram_i => d_ram,
q_ram_o => q_ram,
we_ram_o => we_ram,
re_ram_o => re_ram,
valid_ram_i => valid_ram,
lock_req_o => lock_req,
lock_grant_i => lock_grant,
single_error_p_o => single_error_p,
double_error_p_o => double_error_p
);
lock_grant <= lock_req;
proc_tb: process
constant pattern : std_logic_vector (31 downto 0) := x"0123_4567";
variable xaddr : std_logic_vector (addr_width - 1 downto 0);
begin
wait until rst = '0';
wait until rising_edge(clk);
-- Write words.
for i in 0 to 80 loop
xaddr := std_logic_vector (to_unsigned(i, addr_width));
addr <= xaddr;
din <= pattern;
din (addr_width - 1 downto 0) <= xaddr;
we <= '1';
bwe <= "1111";
force_err <= (others => '0');
if i >= 32 and i < 32 + 39 then
force_err (i - 32) <= '1';
elsif i = 71 then
force_err (31) <= '1';
force_err (21) <= '1';
end if;
re <= '0';
wait until rising_edge(clk);
we <= '0';
wait until rising_edge(clk) and done_w = '1';
end loop;
-- Check words
for i in 0 to 71 loop
xaddr := std_logic_vector (to_unsigned(i, addr_width));
addr <= xaddr;
re <= '1';
wait until rising_edge(clk) and done_r = '1';
re <= '0';
case i is
when 0 to 31 =>
assert dout = pattern (31 downto addr_width) & xaddr severity failure;
assert single_error_p = '0' severity failure;
assert double_error_p = '0' severity failure;
when 32 to 70 =>
assert dout = pattern (31 downto addr_width) & xaddr severity failure;
assert single_error_p = '1' severity failure;
assert double_error_p = '0' severity failure;
when 71 =>
assert single_error_p = '0' severity failure;
assert double_error_p = '1' severity failure;
when others =>
assert false;
end case;
wait until rising_edge(clk) and done_r = '0';
end loop;
report "end of test";
end_of_test <= true;
wait;
end process;
end;
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment