Commit 63f36713 authored by Dimitris Lampridis's avatar Dimitris Lampridis

Merge branch 'release/1.0.4'

parents 9e46223f 42b207a0
.. ..
SPDX-License-Identifier: CC-BY-SA-4.0 SPDX-License-Identifier: CC0-1.0
SPDX-FileCopyrightText: 2019 CERN SPDX-FileCopyrightText: 2019-2020 CERN
========== ==========
Change Log Change Log
========== ==========
Format: `Keep a Changelog <https://keepachangelog.com/en/1.0.0/>`_ - Format inspired by: `Keep a Changelog <https://keepachangelog.com/en/1.0.0/>`_
Versioning: `Semantic Versioning <https://semver.org/spec/v2.0.0.html>`_ - Versioning scheme follows: `Semantic Versioning <https://semver.org/spec/v2.0.0.html>`_
1.0.4 - 2020-03-26
==================
https://www.ohwr.org/project/general-cores/tags/v1.0.4
Added
-----
- [hdl] VHDL functions to convert characters and strings to upper/lower case.
- [sw][i2c] Support for kernel greater than 4.7.
- [hdl] Separate synchroniser and edge detection modules.
- [hdl] 8b10b encoder.
Changed
-------
- [hdl] Rewritten the WB master interface used in simulations.
- [hdl] Reimplement gc_sync_ffs using new synchroniser and edge detectors.
Fixed
-----
- [sw][spi] Align polarity and phase for Rx and Tx.
- [hdl][i2c] Fix reset lock for I2C master.
- [hdl] Avoid cyclic dependencies for log2 ceiling functions.
1.0.3 - 2020-01-15
==================
https://www.ohwr.org/project/general-cores/tags/v1.0.3
[1.0.3] - 2020-01-15
====================
Changed Changed
----- -----
- [sw] add more file to .gitignore - [sw] add more file to .gitignore
[1.0.2] - 2019-10-24 1.0.2 - 2019-10-24
==================== ==================
https://www.ohwr.org/project/general-cores/tags/v1.0.2
Fixed Fixed
----- -----
- [ci] forgot rule to publish RPMs - [ci] forgot rule to publish RPMs
[1.0.1] - 2019-10-24 1.0.1 - 2019-10-24
==================== ==================
https://www.ohwr.org/project/general-cores/tags/v1.0.1
Added Added
----- -----
- [ci] building and publish RPMs automatically on new releases - [ci] building and publish RPMs automatically on new releases
Changed Changed
------- -------
- [sw] Makefiles have been changed to better support RPM generation - [sw] Makefiles have been changed to better support RPM generation
[1.0.0] - 2019-10-21 1.0.0 - 2019-10-21
==================== ==================
https://www.ohwr.org/project/general-cores/tags/v1.0.0
Added Added
----- -----
- First release of general-cores. - First release of general-cores.
...@@ -13,13 +13,22 @@ In [modules/common](modules/common) there are general purpose cores: ...@@ -13,13 +13,22 @@ In [modules/common](modules/common) there are general purpose cores:
* The package [matrix_pkg](modules/common/matrix_pkg.vhd) declares a 2d * The package [matrix_pkg](modules/common/matrix_pkg.vhd) declares a 2d
array of std_logic, and some subprograms to handle it. array of std_logic, and some subprograms to handle it.
* Edge detectors are provided by [gc_posedge](modules/common/gc_posedge.vhd)
and [gc_negedge](modules/common/gc_negedge.vhd).
* For clock-domain crossing or asynchronous signal register, use * For clock-domain crossing or asynchronous signal register, use
[gc_sync_ffs](modules/common/gc_sync_ffs.vhd). It also has an edge [gc_sync](modules/common/gc_sync.vhd). This is the basic synchronizer.
detector. If you also need an edge detector, use
[gc_sync_ffs](modules/common/gc_sync_ffs.vhd).
The other synchronizer [gc_sync_register](modules/common/gc_sync_register.vhd) The other synchronizer [gc_sync_register](modules/common/gc_sync_register.vhd)
is deprecated. It can synchronize multiple signals at the same time but is deprecated. It can synchronize multiple signals at the same time but
doesn't ensure coherency between these signals. doesn't ensure coherency between these signals.
The module [gc_sync_edge](modules/common/gc_sync_edge.vhd) provides a
synchronizer with an (positive or negative) edge detector. The signal
edge is always detected on the rising edge of the clock. This module is
simpler than the gc_sync_ffs module.
To pass words from one clock domain to another, you can use the module To pass words from one clock domain to another, you can use the module
[gc_sync_word_wr](modules/common/gc_sync_word_wr.vhd) for writing data, [gc_sync_word_wr](modules/common/gc_sync_word_wr.vhd) for writing data,
and [gc_sync_word_rd](modules/common/gc_sync_word_rd.vhd) for reading and [gc_sync_word_rd](modules/common/gc_sync_word_rd.vhd) for reading
...@@ -197,6 +206,15 @@ Directory [modules/wishbone](modules/wishbone) contains modules for wishbone. ...@@ -197,6 +206,15 @@ Directory [modules/wishbone](modules/wishbone) contains modules for wishbone.
AT91SAM9x CPU external bus interface. AT91SAM9x CPU external bus interface.
- [wb_axi4lite_bridge](modules/wishbone/wb_axi4lite_bridge) is an axi4lite - [wb_axi4lite_bridge](modules/wishbone/wb_axi4lite_bridge) is an axi4lite
to wishbone bridge to wishbone bridge
- [wb16_to_wb32](modules/wishbone/wb16_to_wb32) is an adapter from a
16 data bit wishbone master to a 32 data bit wishbone slave. It uses
an intermediate register. Refer to the module for how to use it.
* There are modules for axi4 bus
- [axi4lite32_axi4full64_bridge](modules/axi/axi4lite32_axi4full64_bridge) is
a bridge from axi4full64 to axi4lite32. It was defined to interface with
the Vivado PCI-e bridge and doesn't support all the axi4full features
(in particular the burst accesses).
* There a modules to build a bus hierarchy: * There a modules to build a bus hierarchy:
- [wb_bus_fanout](modules/wishbone/wb_bus_fanout) is a simple master to - [wb_bus_fanout](modules/wishbone/wb_bus_fanout) is a simple master to
......
modules = { "local" : [ modules = { "local" : [
"z7_axi_gpio_expander", "z7_axi_gpio_expander",
"axi4lite_wb_bridge", "axi4lite_wb_bridge",
"axi4lite32_axi4full64_bridge",
]} ]}
files = [ files = [
......
files = [
"axi4lite32_axi4full64_bridge.vhd",
];
...@@ -11,6 +11,11 @@ files = [ ...@@ -11,6 +11,11 @@ files = [
"gc_serial_dac.vhd", "gc_serial_dac.vhd",
"gc_sync_ffs.vhd", "gc_sync_ffs.vhd",
"gc_arbitrated_mux.vhd", "gc_arbitrated_mux.vhd",
"gc_sync_register.vhd",
"gc_sync.vhd",
"gc_posedge.vhd",
"gc_negedge.vhd",
"gc_sync_edge.vhd",
"gc_pulse_synchronizer.vhd", "gc_pulse_synchronizer.vhd",
"gc_pulse_synchronizer2.vhd", "gc_pulse_synchronizer2.vhd",
"gc_frequency_meter.vhd", "gc_frequency_meter.vhd",
...@@ -25,10 +30,10 @@ files = [ ...@@ -25,10 +30,10 @@ files = [
"gc_big_adder.vhd", "gc_big_adder.vhd",
"gc_fsm_watchdog.vhd", "gc_fsm_watchdog.vhd",
"gc_bicolor_led_ctrl.vhd", "gc_bicolor_led_ctrl.vhd",
"gc_sync_register.vhd",
"gc_single_reset_gen.vhd", "gc_single_reset_gen.vhd",
"gc_async_signals_input_stage.vhd", "gc_async_signals_input_stage.vhd",
"gc_dec_8b10b.vhd", "gc_dec_8b10b.vhd",
"gc_enc_8b10b.vhd",
"gc_dyn_extend_pulse.vhd", "gc_dyn_extend_pulse.vhd",
"gc_ds182x_interface.vhd", "gc_ds182x_interface.vhd",
"gc_ds182x_readout/gc_ds182x_readout.vhd", "gc_ds182x_readout/gc_ds182x_readout.vhd",
......
This diff is collapsed.
...@@ -22,6 +22,21 @@ ...@@ -22,6 +22,21 @@
-- and limitations under the License. -- and limitations under the License.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- Principle of operation:
--
-- This block counts the number of pulses on CLK_IN_I during a period.
-- At the end of the period, the value is saved and the counter reset.
-- The saved value is available on FREQ_O, which is synchronized with
-- CLK_SYS_I if G_SYNC_OUT is True.
-- The width of the counter is defined by G_COUNTER_BITS.
--
-- - If g_WITH_INTERNAL_TIMEBASE is True:
-- The period is defined by an internal counter that generates a pulse
-- every G_CLK_SYS_FREQ CLK_SYS_I ticks.
--
-- - If g_WITH_INTERNAL_TIMEBASE is False:
-- The period is defined by PPS_P1_I
library ieee; library ieee;
use ieee.std_logic_1164.all; use ieee.std_logic_1164.all;
......
--------------------------------------------------------------------------------
-- CERN BE-CO-HT
-- General Cores Library
-- https://www.ohwr.org/projects/general-cores
--------------------------------------------------------------------------------
--
-- unit name: gc_negedge
--
-- description: Simple falling edge detector. Combinatorial.
--
--------------------------------------------------------------------------------
-- Copyright CERN 2020
--------------------------------------------------------------------------------
-- 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 gc_negedge is
port(
clk_i : in std_logic; -- clock
rst_n_i : in std_logic; -- reset
data_i : in std_logic; -- input
pulse_o : out std_logic); -- falling edge detect output
end entity gc_negedge;
architecture arch of gc_negedge is
signal dff : std_logic;
begin
pulse_o <= not data_i and dff;
process (clk_i)
begin
if rising_edge (clk_i) then
if rst_n_i = '0' then
dff <= '0';
else
dff <= data_i;
end if;
end if;
end process;
end arch;
--------------------------------------------------------------------------------
-- CERN BE-CO-HT
-- General Cores Library
-- https://www.ohwr.org/projects/general-cores
--------------------------------------------------------------------------------
--
-- unit name: gc_posedge
--
-- description: Simple rising edge detector. Combinatorial.
--
--------------------------------------------------------------------------------
-- Copyright CERN 2020
--------------------------------------------------------------------------------
-- 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 gc_posedge is
port(
clk_i : in std_logic; -- clock
rst_n_i : in std_logic; -- reset
data_i : in std_logic; -- input
pulse_o : out std_logic); -- positive edge detect output
end entity gc_posedge;
architecture arch of gc_posedge is
signal dff : std_logic;
begin
pulse_o <= data_i and not dff;
process (clk_i)
begin
if rising_edge (clk_i) then
if rst_n_i = '0' then
dff <= '0';
else
dff <= data_i;
end if;
end if;
end process;
end arch;
...@@ -89,12 +89,18 @@ begin -- rtl ...@@ -89,12 +89,18 @@ begin -- rtl
d_ack_d0 <= d_ack; d_ack_d0 <= d_ack;
if ready = '1' and d_p_i = '1' and d_p_d0 = '0'then 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'; in_ext <= '1';
-- Clear ack and ready!
d_ack <= '0'; d_ack <= '0';
ready <= '0'; ready <= '0';
elsif in_ext = '1' and out_feedback = '1' then elsif in_ext = '1' and out_feedback = '1' then
-- Pulse has been transfered, clear the input.
in_ext <= '0'; in_ext <= '0';
elsif in_ext = '0' and out_feedback = '0' then elsif in_ext = '0' and out_feedback = '0' then
-- Clear transfered. Done.
-- This is also the steady state.
d_ack <= '1'; d_ack <= '1';
ready <= '1'; ready <= '1';
end if; end if;
......
--------------------------------------------------------------------------------
-- CERN BE-CO-HT
-- General Cores Library
-- https://www.ohwr.org/projects/general-cores
--------------------------------------------------------------------------------
--
-- unit name: gc_sync
--
-- description: Elementary synchronizer chain using two flip-flops.
--
--------------------------------------------------------------------------------
-- 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;
entity gc_sync is
generic(
g_sync_edge : string := "positive");
port (
clk_i : in std_logic;
rst_n_a_i : in std_logic;
d_i : in std_logic;
q_o : out std_logic);
end gc_sync;
-- make Altera Quartus quiet regarding unknown attributes:
-- altera message_off 10335
architecture arch of gc_sync is
-- Use an intermediate signal with a particular name and a keep attribute
-- so that it can be referenced in the constraints in order to ignore
-- timing (TIG) on that signal.
signal gc_sync_ffs_in : std_logic;
signal sync0, sync1 : std_logic;
attribute rloc : string;
attribute rloc of sync0 : signal is "X0Y0";
attribute rloc of sync1 : signal is "X0Y0";
attribute shreg_extract : string;
attribute shreg_extract of sync0 : signal is "no";
attribute shreg_extract of sync1 : signal is "no";
attribute keep : string;
attribute keep of gc_sync_ffs_in : signal is "true";
attribute keep of sync0 : signal is "true";
attribute keep of sync1 : signal is "true";
attribute keep_hierarchy : string;
attribute keep_hierarchy of arch : architecture is "true";
attribute async_reg : string;
attribute async_reg of sync0 : signal is "true";
attribute async_reg of sync1 : signal is "true";
begin
assert g_sync_edge = "positive" or g_sync_edge = "negative" severity failure;
gc_sync_ffs_in <= d_i;
sync_posedge : if (g_sync_edge = "positive") generate
process(clk_i, rst_n_a_i)
begin
if rst_n_a_i = '0' then
sync1 <= '0';
sync0 <= '0';
elsif rising_edge(clk_i) then
sync0 <= gc_sync_ffs_in;
sync1 <= sync0;
end if;
end process;
end generate sync_posedge;
sync_negedge : if(g_sync_edge = "negative") generate
process(clk_i, rst_n_a_i)
begin
if rst_n_a_i = '0' then
sync1 <= '0';
sync0 <= '0';
elsif falling_edge(clk_i) then
sync0 <= gc_sync_ffs_in;
sync1 <= sync0;
end if;
end process;
end generate sync_negedge;
q_o <= sync1;
end arch;
--------------------------------------------------------------------------------
-- CERN BE-CO-HT
-- General Cores Library
-- https://www.ohwr.org/projects/general-cores
--------------------------------------------------------------------------------
--
-- unit name: gc_sync_edge
--
-- description: Synchronizer chain and edge detector.
-- All the registers in the chain are cleared at reset.
--
--------------------------------------------------------------------------------
-- Copyright CERN 2010-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;
entity gc_sync_edge is
generic(
g_edge : string := "positive");
port(
clk_i : in std_logic; -- clock from the destination clock domain
rst_n_a_i : in std_logic; -- async reset
data_i : in std_logic; -- async input
synced_o : out std_logic; -- synchronized output
pulse_o : out std_logic); -- edge detect output
end entity gc_sync_edge;
architecture arch of gc_sync_edge is
signal sync : std_logic;
begin
inst_sync : entity work.gc_sync
port map (
clk_i => clk_i,
rst_n_a_i => rst_n_a_i,
d_i => data_i,
q_o => sync);
assert g_edge = "positive" or g_edge = "negative" severity FAILURE;
sync_posedge : if g_edge = "positive" generate
inst_pedge : entity work.gc_posedge
port map (
clk_i => clk_i,
rst_n_i => rst_n_a_i,
data_i => sync,
pulse_o => pulse_o);
end generate;
sync_negedge : if g_edge = "negative" generate
inst_pedge : entity work.gc_negedge
port map (
clk_i => clk_i,
rst_n_i => rst_n_a_i,
data_i => sync,
pulse_o => pulse_o);
end generate;
end architecture arch;
...@@ -31,66 +31,53 @@ entity gc_sync_ffs is ...@@ -31,66 +31,53 @@ entity gc_sync_ffs is
g_sync_edge : string := "positive"); g_sync_edge : string := "positive");
port( port(
clk_i : in std_logic; -- clock from the destination clock domain clk_i : in std_logic; -- clock from the destination clock domain
rst_n_i : in std_logic; -- reset rst_n_i : in std_logic; -- async reset
data_i : in std_logic; -- async input data_i : in std_logic; -- async input
synced_o : out std_logic; -- synchronized output synced_o : out std_logic; -- synchronized output
npulse_o : out std_logic; -- negative edge detect output npulse_o : out std_logic; -- negative edge detect output
ppulse_o : out std_logic); -- positive edge detect output ppulse_o : out std_logic); -- positive edge detect output
end entity gc_sync_ffs; end entity gc_sync_ffs;
-- make Altera Quartus quiet regarding unknown attributes:
-- altera message_off 10335
architecture arch of gc_sync_ffs is architecture arch of gc_sync_ffs is
signal sync0, sync1, sync2 : std_logic; signal sync, npulse, ppulse : std_logic;
signal gc_sync_ffs_in : std_logic;
attribute shreg_extract : string;
attribute shreg_extract of sync0 : signal is "no";
attribute shreg_extract of sync1 : signal is "no";
attribute shreg_extract of sync2 : signal is "no";
attribute keep : string;
attribute keep of sync0 : signal is "true";
attribute keep of sync1 : signal is "true";
attribute rloc : string;
attribute rloc of sync0 : signal is "X0Y0";
attribute rloc of sync1 : signal is "X0Y0";
attribute keep of gc_sync_ffs_in : signal is "true";
-- synchronizer attribute for Vivado
attribute ASYNC_REG : string;
attribute ASYNC_REG of sync0 : signal is "true";
attribute ASYNC_REG of sync1 : signal is "true";
attribute ASYNC_REG of sync2 : signal is "true";
begin begin
-- rename data_i to something we can use as wildcard cmp_gc_sync : entity work.gc_sync
-- in timing constraints generic map (
gc_sync_ffs_in <= data_i; g_sync_edge => g_sync_edge)
port map (
clk_i => clk_i,
rst_n_a_i => rst_n_i,
d_i => data_i,
q_o => sync);
cmp_gc_posedge : entity work.gc_posedge
port map (
clk_i => clk_i,
rst_n_i => rst_n_i,
data_i => sync,
pulse_o => ppulse);
cmp_gc_negedge : entity work.gc_negedge
port map (
clk_i => clk_i,
rst_n_i => rst_n_i,
data_i => sync,
pulse_o => npulse);
sync_posedge : if (g_sync_edge = "positive") generate sync_posedge : if (g_sync_edge = "positive") generate
process(clk_i, rst_n_i) process(clk_i, rst_n_i)
begin begin
if(rst_n_i = '0') then if(rst_n_i = '0') then
sync0 <= '0';
sync1 <= '0';
sync2 <= '0';
synced_o <= '0'; synced_o <= '0';
npulse_o <= '0'; npulse_o <= '0';
ppulse_o <= '0'; ppulse_o <= '0';
elsif rising_edge(clk_i) then elsif rising_edge(clk_i) then
sync0 <= gc_sync_ffs_in; synced_o <= sync;
sync1 <= sync0; npulse_o <= npulse;
sync2 <= sync1; ppulse_o <= ppulse;
synced_o <= sync1;
npulse_o <= sync2 and not sync1;
ppulse_o <= not sync2 and sync1;
end if; end if;
end process; end process;
end generate sync_posedge; end generate sync_posedge;
...@@ -99,19 +86,13 @@ begin ...@@ -99,19 +86,13 @@ begin
process(clk_i, rst_n_i) process(clk_i, rst_n_i)
begin begin
if(rst_n_i = '0') then if(rst_n_i = '0') then
sync0 <= '0';
sync1 <= '0';
sync2 <= '0';
synced_o <= '0'; synced_o <= '0';
npulse_o <= '0'; npulse_o <= '0';
ppulse_o <= '0'; ppulse_o <= '0';
elsif falling_edge(clk_i) then elsif falling_edge(clk_i) then
sync0 <= gc_sync_ffs_in; synced_o <= sync;
sync1 <= sync0; npulse_o <= npulse;
sync2 <= sync1; ppulse_o <= ppulse;
synced_o <= sync1;
npulse_o <= sync2 and not sync1;
ppulse_o <= not sync2 and sync1;
end if; end if;
end process; end process;
end generate sync_negedge; end generate sync_negedge;
......
...@@ -53,7 +53,10 @@ architecture rtl of gc_sync_register is ...@@ -53,7 +53,10 @@ architecture rtl of gc_sync_register is
attribute keep of gc_sync_register_in : signal is "true"; attribute keep of gc_sync_register_in : signal is "true";
attribute keep of sync0 : signal is "true"; attribute keep of sync0 : signal is "true";
attribute keep of sync1 : signal is "true"; attribute keep of sync1 : signal is "true";
attribute keep_hierarchy : string;
attribute keep_hierarchy of rtl : architecture is "true";
attribute async_reg : string; attribute async_reg : string;
attribute async_reg of gc_sync_register_in : signal is "true"; attribute async_reg of gc_sync_register_in : signal is "true";
attribute async_reg of sync0 : signal is "true"; attribute async_reg of sync0 : signal is "true";
......
...@@ -27,10 +27,60 @@ library ieee; ...@@ -27,10 +27,60 @@ library ieee;
use ieee.std_logic_1164.all; use ieee.std_logic_1164.all;
use ieee.numeric_std.all; use ieee.numeric_std.all;
use work.genram_pkg.all;
package gencores_pkg is package gencores_pkg is
--============================================================================
-- Procedures and functions
--============================================================================
procedure f_rr_arbitrate (
signal req : in std_logic_vector;
signal pre_grant : in std_logic_vector;
signal grant : out std_logic_vector);
function f_onehot_decode(x : std_logic_vector; size : integer) return std_logic_vector;
function f_big_ripple(a, b : std_logic_vector; c : std_logic) return std_logic_vector;
function f_gray_encode(x : std_logic_vector) return std_logic_vector;
function f_gray_decode(x : std_logic_vector; step : natural) return std_logic_vector;
function f_log2_ceil(N : natural) return positive;
-- kept for backwards compatibility, same as f_log2_ceil()
function log2_ceil(N : natural) return positive;
function f_bool2int (b : boolean) return natural;
function f_int2bool (n : natural) return boolean;
-- Convert a boolean to std_logic ('1' for True, '0' for False).
function f_to_std_logic(b : boolean) return std_logic;
-- Reduce-OR an std_logic_vector to std_logic
function f_reduce_or (x : std_logic_vector) return std_logic;
-- Character/String to std_logic_vector
function f_to_std_logic_vector (c : character) return std_logic_vector;
function f_to_std_logic_vector (s : string) return std_logic_vector;
-- Functions for short-hand if assignments
function f_pick (cond : boolean; if_1 : std_logic; if_0 : std_logic)
return std_logic;
function f_pick (cond : boolean; if_1 : std_logic_vector; if_0 : std_logic_vector)
return std_logic_vector;
function f_pick (cond : std_logic; if_1 : std_logic; if_0 : std_logic)
return std_logic;
function f_pick (cond : std_logic; if_1 : std_logic_vector; if_0 : std_logic_vector)
return std_logic_vector;
-- Functions to convert characters and strings to upper/lower case
function to_upper(c : character) return character;
function to_lower(c : character) return character;
function to_upper(s : string) return string;
function to_lower(s : string) return string;
-- Bit reversal function
function f_reverse_vector (a : in std_logic_vector) return std_logic_vector;
--============================================================================ --============================================================================
-- Component instantiations -- Component instantiations
--============================================================================ --============================================================================
...@@ -198,6 +248,46 @@ package gencores_pkg is ...@@ -198,6 +248,46 @@ package gencores_pkg is
ppulse_o : out std_logic); ppulse_o : out std_logic);
end component; end component;
component gc_sync is
generic (
g_sync_edge : string := "positive");
port (
clk_i : in std_logic;
rst_n_a_i : in std_logic;
d_i : in std_logic;
q_o : out std_logic);
end component gc_sync;
component gc_sync_edge is
generic (
g_edge : string := "positive");
port (
clk_i : in std_logic;
rst_n_a_i : in std_logic;
data_i : in std_logic;
synced_o : out std_logic;
pulse_o : out std_logic);
end component gc_sync_edge;
------------------------------------------------------------------------------
-- Edge detectors
------------------------------------------------------------------------------
component gc_negedge is
port (
clk_i : in std_logic;
rst_n_i : in std_logic;
data_i : in std_logic;
pulse_o : out std_logic);
end component gc_negedge;
component gc_posedge is
port (
clk_i : in std_logic;
rst_n_i : in std_logic;
data_i : in std_logic;
pulse_o : out std_logic);
end component gc_posedge;
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
-- Pulse synchroniser -- Pulse synchroniser
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
...@@ -295,7 +385,7 @@ package gencores_pkg is ...@@ -295,7 +385,7 @@ package gencores_pkg is
d_req_o : out std_logic_vector(g_num_inputs-1 downto 0); d_req_o : out std_logic_vector(g_num_inputs-1 downto 0);
q_o : out std_logic_vector(g_width-1 downto 0); q_o : out std_logic_vector(g_width-1 downto 0);
q_valid_o : out std_logic; q_valid_o : out std_logic;
q_input_id_o : out std_logic_vector(f_log2_size(g_num_inputs)-1 downto 0)); q_input_id_o : out std_logic_vector(f_log2_ceil(g_num_inputs)-1 downto 0));
end component; end component;
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
...@@ -690,42 +780,6 @@ package gencores_pkg is ...@@ -690,42 +780,6 @@ package gencores_pkg is
counter_o : out std_logic_vector(g_bits downto 0)); counter_o : out std_logic_vector(g_bits downto 0));
end component gc_async_counter_diff; end component gc_async_counter_diff;
--============================================================================
-- Procedures and functions
--============================================================================
procedure f_rr_arbitrate (
signal req : in std_logic_vector;
signal pre_grant : in std_logic_vector;
signal grant : out std_logic_vector);
function f_onehot_decode(x : std_logic_vector; size : integer) return std_logic_vector;
function f_big_ripple(a, b : std_logic_vector; c : std_logic) return std_logic_vector;
function f_gray_encode(x : std_logic_vector) return std_logic_vector;
function f_gray_decode(x : std_logic_vector; step : natural) return std_logic_vector;
function log2_ceil(N : natural) return positive;
function f_bool2int (b : boolean) return natural;
function f_int2bool (n : natural) return boolean;
-- Convert a boolean to std_logic ('1' for True, '0' for False).
function f_to_std_logic(b : boolean) return std_logic;
-- Reduce-OR an std_logic_vector to std_logic
function f_reduce_or (x : std_logic_vector) return std_logic;
-- Character/String to std_logic_vector
function f_to_std_logic_vector (c : character) return std_logic_vector;
function f_to_std_logic_vector (s : string) return std_logic_vector;
-- Functions for short-hand if assignments
function f_pick (cond : boolean; if_1 : std_logic; if_0 : std_logic)
return std_logic;
function f_pick (cond : boolean; if_1 : std_logic_vector; if_0 : std_logic_vector)
return std_logic_vector;
function f_pick (cond : std_logic; if_1 : std_logic; if_0 : std_logic)
return std_logic;
function f_pick (cond : std_logic; if_1 : std_logic_vector; if_0 : std_logic_vector)
return std_logic_vector;
end package; end package;
package body gencores_pkg is package body gencores_pkg is
...@@ -827,17 +881,22 @@ package body gencores_pkg is ...@@ -827,17 +881,22 @@ package body gencores_pkg is
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
-- Returns log of 2 of a natural number -- Returns log of 2 of a natural number
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
function log2_ceil(N : natural) return positive is function f_log2_ceil(N : natural) return positive is
begin begin
if N <= 2 then if N <= 2 then
return 1; return 1;
elsif N mod 2 = 0 then elsif N mod 2 = 0 then
return 1 + log2_ceil(N/2); return 1 + f_log2_ceil(N/2);
else else
return 1 + log2_ceil((N+1)/2); return 1 + f_log2_ceil((N+1)/2);
end if; end if;
end; end;
-- kept for backwards compatibility
function log2_ceil(N : natural) return positive is
begin
return f_log2_ceil(N);
end;
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
-- Converts a boolean to natural integer (false -> 0, true -> 1) -- Converts a boolean to natural integer (false -> 0, true -> 1)
...@@ -954,4 +1013,59 @@ package body gencores_pkg is ...@@ -954,4 +1013,59 @@ package body gencores_pkg is
return f_pick (f_to_std_logic(cond), if_1, if_0); return f_pick (f_to_std_logic(cond), if_1, if_0);
end function f_pick; end function f_pick;
------------------------------------------------------------------------------
-- Functions to convert characters and strings to upper/lower case
------------------------------------------------------------------------------
function to_upper(c : character) return character is
variable i : integer;
begin
i := character'pos(c);
if (i > 96 and i < 123) then
i := i - 32;
end if;
return character'val(i);
end function to_upper;
function to_lower(c : character) return character is
variable i : integer;
begin
i := character'pos(c);
if (i > 64 and i < 91) then
i := i + 32;
end if;
return character'val(i);
end function to_lower;
function to_upper(s : string) return string is
variable uppercase : string (s'range);
begin
for i in s'range loop
uppercase(i) := to_upper(s(i));
end loop;
return uppercase;
end to_upper;
function to_lower(s : string) return string is
variable lowercase : string (s'range);
begin
for i in s'range loop
lowercase(i) := to_lower(s(i));
end loop;
return lowercase;
end to_lower;
------------------------------------------------------------------------------
-- Vector bit reversal
------------------------------------------------------------------------------
function f_reverse_vector (a : in std_logic_vector) return std_logic_vector is
variable v_result : std_logic_vector(a'reverse_range);
begin
for i in a'range loop
v_result(i) := a(i);
end loop;
return v_result;
end function f_reverse_vector;
end gencores_pkg; end gencores_pkg;
...@@ -83,6 +83,10 @@ entity inferred_async_fifo is ...@@ -83,6 +83,10 @@ entity inferred_async_fifo is
rd_count_o : out std_logic_vector(f_log2_size(g_size)-1 downto 0) rd_count_o : out std_logic_vector(f_log2_size(g_size)-1 downto 0)
); );
attribute keep_hierarchy : string;
attribute keep_hierarchy of
inferred_async_fifo : entity is "true";
end inferred_async_fifo; end inferred_async_fifo;
......
...@@ -74,6 +74,10 @@ entity inferred_async_fifo_dual_rst is ...@@ -74,6 +74,10 @@ entity inferred_async_fifo_dual_rst is
rd_count_o : out std_logic_vector(f_log2_size(g_size)-1 downto 0) rd_count_o : out std_logic_vector(f_log2_size(g_size)-1 downto 0)
); );
attribute keep_hierarchy : string;
attribute keep_hierarchy of
inferred_async_fifo_dual_rst : entity is "true";
end inferred_async_fifo_dual_rst; end inferred_async_fifo_dual_rst;
......
...@@ -72,13 +72,13 @@ end inferred_sync_fifo; ...@@ -72,13 +72,13 @@ end inferred_sync_fifo;
architecture syn of inferred_sync_fifo is architecture syn of inferred_sync_fifo is
constant c_pointer_width : integer := f_log2_size(g_size); constant c_pointer_width : integer := f_log2_size(g_size);
signal rd_ptr, wr_ptr, wr_ptr_d0, rd_ptr_muxed : unsigned(c_pointer_width-1 downto 0); signal rd_ptr, wr_ptr, rd_ptr_muxed : unsigned(c_pointer_width-1 downto 0);
signal usedw : unsigned(c_pointer_width downto 0); signal usedw : unsigned(c_pointer_width downto 0);
signal full, empty : std_logic; signal full, empty : std_logic;
signal q_int : std_logic_vector(g_data_width-1 downto 0);
signal we_int, rd_int : std_logic; signal we_int, rd_int : std_logic;
signal guard_bit : std_logic; signal guard_bit : std_logic;
signal q_comb : std_logic_vector(g_data_width-1 downto 0); signal q_comb : std_logic_vector(g_data_width-1 downto 0);
......
...@@ -26,6 +26,9 @@ library ieee; ...@@ -26,6 +26,9 @@ library ieee;
use ieee.std_logic_1164.all; use ieee.std_logic_1164.all;
use ieee.numeric_std.all; use ieee.numeric_std.all;
library work;
use work.gencores_pkg.all;
package genram_pkg is package genram_pkg is
function f_log2_size (A : natural) return natural; function f_log2_size (A : natural) return natural;
...@@ -248,14 +251,10 @@ end genram_pkg; ...@@ -248,14 +251,10 @@ end genram_pkg;
package body genram_pkg is package body genram_pkg is
-- kept for backwards compatibility
function f_log2_size (A : natural) return natural is function f_log2_size (A : natural) return natural is
begin begin
for I in 1 to 64 loop -- Works for up to 64 bits return f_log2_ceil(A);
if (2**I >= A) then
return(I);
end if;
end loop;
return(63);
end function f_log2_size; end function f_log2_size;
function f_gen_dummy_vec (val : std_logic; size : natural) return std_logic_vector is function f_gen_dummy_vec (val : std_logic; size : natural) return std_logic_vector is
......
...@@ -118,17 +118,12 @@ architecture syn of generic_dpram_dualclock is ...@@ -118,17 +118,12 @@ architecture syn of generic_dpram_dualclock is
return false; return false;
-- synthesis translate_on -- synthesis translate_on
return true; return true;
end f_is_synthesis; end f_is_synthesis;
shared variable ram : t_ram_type := f_file_to_ramtype; shared variable ram : t_ram_type := f_file_to_ramtype;
signal s_we_a : std_logic_vector(c_num_bytes-1 downto 0); signal s_we_a : std_logic_vector(c_num_bytes-1 downto 0);
signal s_ram_in_a : std_logic_vector(g_data_width-1 downto 0);
signal s_we_b : std_logic_vector(c_num_bytes-1 downto 0); signal s_we_b : std_logic_vector(c_num_bytes-1 downto 0);
signal s_ram_in_b : std_logic_vector(g_data_width-1 downto 0);
signal clka_int : std_logic;
signal clkb_int : std_logic;
signal wea_rep, web_rep : std_logic_vector(c_num_bytes-1 downto 0); signal wea_rep, web_rep : std_logic_vector(c_num_bytes-1 downto 0);
...@@ -177,7 +172,7 @@ begin ...@@ -177,7 +172,7 @@ begin
end loop; end loop;
end if; end if;
end process; end process;
...@@ -266,6 +261,6 @@ begin ...@@ -266,6 +261,6 @@ begin
end if; end if;
end process; end process;
end generate gen_without_byte_enable_nochange; end generate gen_without_byte_enable_nochange;
end syn; end syn;
...@@ -123,9 +123,7 @@ architecture syn of generic_dpram_sameclock is ...@@ -123,9 +123,7 @@ architecture syn of generic_dpram_sameclock is
shared variable ram : t_ram_type := f_file_to_ramtype; shared variable ram : t_ram_type := f_file_to_ramtype;
signal s_we_a : std_logic_vector(c_num_bytes-1 downto 0); signal s_we_a : std_logic_vector(c_num_bytes-1 downto 0);
signal s_ram_in_a : std_logic_vector(g_data_width-1 downto 0);
signal s_we_b : std_logic_vector(c_num_bytes-1 downto 0); signal s_we_b : std_logic_vector(c_num_bytes-1 downto 0);
signal s_ram_in_b : std_logic_vector(g_data_width-1 downto 0);
signal wea_rep, web_rep : std_logic_vector(c_num_bytes-1 downto 0); signal wea_rep, web_rep : std_logic_vector(c_num_bytes-1 downto 0);
......
...@@ -26,6 +26,7 @@ modules = { "local" : [ ...@@ -26,6 +26,7 @@ modules = { "local" : [
"wb_ds182x_readout", "wb_ds182x_readout",
"wb_metadata", "wb_metadata",
"wb_split", "wb_split",
"wb16_to_wb32",
"wbgen2", "wbgen2",
"wbgenplus", "wbgenplus",
]} ]}
......
--------------------------------------------------------------------------------
-- CERN BE-CO-HT
-- WR2RF_VME core
-- https://ohwr.org/project/vme-rf-wr-bobr
--------------------------------------------------------------------------------
--
-- unit name: wb16_to_wb32
--
-- description: Bridge wishbone data width by using a register for the upper 16
-- bits.
-- In order to atomically read a 32 bit word at address ADDR:
-- * read the 16 LSB word at address ADDR
-- * read the 16 MSB word at address ADDR+2
-- In order to atomically write a 32 bit word at address ADDR:
-- * write the 16 MSB word at address ADDR+2
-- * write the 16 LSB word at address ADDR
--
--------------------------------------------------------------------------------
-- Copyright (c) 2019 CERN (home.cern)
--------------------------------------------------------------------------------
-- 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.wishbone_pkg.all;
entity wb16_to_wb32 is
port (
clk_i : in std_logic;
rst_n_i : in std_logic;
wb16_i : in t_wishbone_slave_in;
wb16_o : out t_wishbone_slave_out;
wb32_i : in t_wishbone_master_in;
wb32_o : out t_wishbone_master_out
);
end;
architecture arch of wb16_to_wb32 is
signal datah : std_logic_vector(15 downto 0);
signal stall : std_logic;
signal we : std_logic;
signal ack : std_logic;
begin
wb16_o.stall <= stall or ack;
wb32_o.dat (31 downto 16) <= datah;
wb16_o.rty <= '0';
wb16_o.err <= '0';
wb16_o.ack <= ack;
process (clk_i) is
begin
if rising_edge(clk_i) then
if rst_n_i = '0' then
datah <= (others => '0');
stall <= '0';
ack <= '0';
wb32_o.cyc <= '0';
wb32_o.stb <= '0';
else
if stall = '0' then
-- Ready.
ack <= '0';
if wb16_i.stb = '1' and wb16_i.cyc = '1' and ack = '0' then
if wb16_i.adr(1) = '1' then
-- Access to DATAH.
if wb16_i.we = '1' then
-- Write.
if wb16_i.sel(0) = '1' then
datah (7 downto 0) <= wb16_i.dat(7 downto 0);
end if;
if wb16_i.sel(1) = '1' then
datah (15 downto 8) <= wb16_i.dat(15 downto 8);
end if;
else
-- Read
wb16_o.dat(15 downto 0) <= datah;
end if;
ack <= '1';
else
-- Access to the device.
stall <= '1';
we <= wb16_i.we;
wb32_o.cyc <= '1';
wb32_o.stb <= '1';
wb32_o.adr <= wb16_i.adr(31 downto 2) & "00";
wb32_o.dat (15 downto 0) <= wb16_i.dat(15 downto 0);
wb32_o.we <= wb16_i.we;
wb32_o.sel <= "11" & wb16_i.sel (1 downto 0); -- Humm...
end if;
end if;
else
-- Stall = 1, waiting for the answer.
if wb32_i.ack = '1' then
wb16_o.dat (15 downto 0) <= wb32_i.dat (15 downto 0);
if we = '0' then
datah <= wb32_i.dat (31 downto 16);
end if;
wb32_o.cyc <= '0';
wb32_o.stb <= '0';
ack <= '1';
stall <= '0';
end if;
end if;
end if;
end if;
end process;
end arch;
...@@ -157,7 +157,7 @@ architecture structural of i2c_master_top is ...@@ -157,7 +157,7 @@ architecture structural of i2c_master_top is
signal sr : std_logic_vector(7 downto 0); -- status register signal sr : std_logic_vector(7 downto 0); -- status register
-- internal reset signal -- internal reset signal
signal rst_i : std_logic; signal arst_n : std_logic;
-- wishbone write access -- wishbone write access
signal wb_wacc : std_logic; signal wb_wacc : std_logic;
...@@ -190,7 +190,7 @@ architecture structural of i2c_master_top is ...@@ -190,7 +190,7 @@ architecture structural of i2c_master_top is
begin begin
-- generate internal reset signal -- generate internal reset signal
rst_i <= not wb_rst_i; --arst_i xor ARST_LVL; arst_n <= arst_i xor ARST_LVL;
-- generate acknowledge output signal -- generate acknowledge output signal
gen_ack_o : process(wb_clk_i) gen_ack_o : process(wb_clk_i)
...@@ -228,9 +228,9 @@ begin ...@@ -228,9 +228,9 @@ begin
-- generate registers (CR, SR see below) -- generate registers (CR, SR see below)
gen_regs: process(rst_i, wb_clk_i) gen_regs: process(arst_n, wb_clk_i)
begin begin
if (rst_i = '0') then if (arst_n = '0') then
prer <= (others => '1'); prer <= (others => '1');
ctr <= (others => '0'); ctr <= (others => '0');
txr <= (others => '0'); txr <= (others => '0');
...@@ -268,9 +268,9 @@ begin ...@@ -268,9 +268,9 @@ begin
-- generate command register -- generate command register
gen_cr: process(rst_i, wb_clk_i) gen_cr: process(arst_n, wb_clk_i)
begin begin
if (rst_i = '0') then if (arst_n = '0') then
cr <= (others => '0'); cr <= (others => '0');
elsif (wb_clk_i'event and wb_clk_i = '1') then elsif (wb_clk_i'event and wb_clk_i = '1') then
if (wb_rst_i = '1') then if (wb_rst_i = '1') then
...@@ -309,7 +309,7 @@ begin ...@@ -309,7 +309,7 @@ begin
port map ( port map (
clk => wb_clk_i, clk => wb_clk_i,
rst => wb_rst_i, rst => wb_rst_i,
nReset => rst_i, nReset => arst_n,
ena => core_en, ena => core_en,
clk_cnt => prer, clk_cnt => prer,
start => sta, start => sta,
...@@ -346,9 +346,9 @@ begin ...@@ -346,9 +346,9 @@ begin
st_irq_block : block st_irq_block : block
begin begin
-- generate status register bits -- generate status register bits
gen_sr_bits: process (wb_clk_i, rst_i) gen_sr_bits: process (arst_n, wb_clk_i)
begin begin
if (rst_i = '0') then if (arst_n = '0') then
al <= '0'; al <= '0';
rxack <= '0'; rxack <= '0';
tip <= '0'; tip <= '0';
...@@ -371,9 +371,9 @@ begin ...@@ -371,9 +371,9 @@ begin
end process gen_sr_bits; end process gen_sr_bits;
-- generate interrupt request signals -- generate interrupt request signals
gen_irq: process (wb_clk_i, rst_i) gen_irq: process (arst_n, wb_clk_i)
begin begin
if (rst_i = '0') then if (arst_n = '0') then
inta_o <= '0'; inta_o <= '0';
elsif (wb_clk_i'event and wb_clk_i = '1') then elsif (wb_clk_i'event and wb_clk_i = '1') then
if (wb_rst_i = '1') then if (wb_rst_i = '1') then
......
...@@ -28,9 +28,6 @@ library ieee; ...@@ -28,9 +28,6 @@ library ieee;
use ieee.std_logic_1164.all; use ieee.std_logic_1164.all;
use ieee.numeric_std.all; use ieee.numeric_std.all;
library work;
use work.genram_pkg.all;
package wishbone_pkg is package wishbone_pkg is
constant c_wishbone_address_width : integer := 32; constant c_wishbone_address_width : integer := 32;
......
This diff is collapsed.
...@@ -28,6 +28,91 @@ ...@@ -28,6 +28,91 @@
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/jiffies.h> #include <linux/jiffies.h>
struct ocores_i2c;
static int ohwr_i2c_mux_select(struct ocores_i2c *i2c, u32 num);
static int ohwr_i2c_mux_deselect(struct ocores_i2c *i2c, u32 num);
#if KERNEL_VERSION(4, 7, 0) > LINUX_VERSION_CODE
struct i2c_mux_core {
struct i2c_adapter *parent;
struct device *dev;
void *priv;
unsigned int max_adapters;
struct i2c_adapter *adapter[0];
};
/**
* It selects the I2C bus to use and lock it
*/
static int ocores_i2c_mux_select_old_api(struct i2c_adapter *adap,
void *priv, u32 num)
{
struct ocores_i2c *i2c = priv;
return ohwr_i2c_mux_select(i2c, num);
}
/**
* It unlocks the bus so that it can be changed.
*/
static int ocores_i2c_mux_deselect_old_api(struct i2c_adapter *adap,
void *priv, u32 num)
{
struct ocores_i2c *i2c = priv;
return ohwr_i2c_mux_deselect(i2c, num);
}
struct i2c_mux_core *i2c_mux_alloc(struct i2c_adapter *parent,
struct device *dev, int max_adapters,
int sizeof_priv, u32 flags,
int (*select)(struct i2c_mux_core *, u32),
int (*deselect)(struct i2c_mux_core *, u32))
{
struct i2c_mux_core *muxc;
muxc = devm_kzalloc(dev, sizeof(*muxc) +
sizeof(*muxc->adapter) * max_adapters +
sizeof_priv, GFP_KERNEL);
if (!muxc)
return NULL;
if (sizeof_priv)
muxc->priv = &muxc->adapter[max_adapters];
muxc->parent = parent;
muxc->dev = dev;
muxc->max_adapters = max_adapters;
return muxc;
}
int i2c_mux_add_adapter(struct i2c_mux_core *muxc,
u32 force_nr, u32 chan_id,
unsigned int class)
{
muxc->adapter[chan_id] = i2c_add_mux_adapter(muxc->parent,
muxc->dev,
muxc->priv,
force_nr,
chan_id,
#if KERNEL_VERSION(3, 7, 0) <= LINUX_VERSION_CODE
class, /* class */
#endif
ocores_i2c_mux_select_old_api,
ocores_i2c_mux_deselect_old_api);
return muxc->adapter[chan_id] ? 0 : -EINVAL;
}
void i2c_mux_del_adapters(struct i2c_mux_core *muxc)
{
int i;
for (i = 0; i < muxc->max_adapters; ++i)
i2c_del_mux_adapter(muxc->adapter[i]);
}
#endif
#define OCORES_FLAG_POLL BIT(0) #define OCORES_FLAG_POLL BIT(0)
/** /**
...@@ -41,8 +126,7 @@ struct ocores_i2c { ...@@ -41,8 +126,7 @@ struct ocores_i2c {
unsigned long flags; unsigned long flags;
wait_queue_head_t wait; wait_queue_head_t wait;
struct i2c_adapter adap; struct i2c_adapter adap;
struct i2c_adapter **adap_mux; struct i2c_mux_core *adap_mux;
unsigned int n_adap_mux;
struct i2c_msg *msg; struct i2c_msg *msg;
int pos; int pos;
int nmsgs; int nmsgs;
...@@ -578,14 +662,8 @@ static const struct platform_device_id ocores_id_table[] = { ...@@ -578,14 +662,8 @@ static const struct platform_device_id ocores_id_table[] = {
}; };
MODULE_DEVICE_TABLE(id_table, ocores_id_table); MODULE_DEVICE_TABLE(id_table, ocores_id_table);
static int ohwr_i2c_mux_select(struct ocores_i2c *i2c, u32 num)
/**
* It selects the I2C bus to use and lock it
*/
static int ocores_i2c_mux_select(struct i2c_adapter *adap,
void *priv, u32 num)
{ {
struct ocores_i2c *i2c = priv;
u8 mux; u8 mux;
mux = oc_getreg(i2c, OCI2C_OHWR_MUX); mux = oc_getreg(i2c, OCI2C_OHWR_MUX);
...@@ -601,57 +679,69 @@ static int ocores_i2c_mux_select(struct i2c_adapter *adap, ...@@ -601,57 +679,69 @@ static int ocores_i2c_mux_select(struct i2c_adapter *adap,
return 0; return 0;
} }
static int ohwr_i2c_mux_deselect(struct ocores_i2c *i2c, u32 num)
/**
* It unlocks the bus so that it can be changed.
*/
static int ocores_i2c_mux_deselect(struct i2c_adapter *adap,
void *priv, u32 num)
{ {
struct ocores_i2c *i2c = priv;
u8 mux; u8 mux;
/* Unlock bus selection */ /* Unlock bus selection */
mux = oc_getreg(i2c, OCI2C_OHWR_MUX); mux = oc_getreg(i2c, OCI2C_OHWR_MUX);
if (unlikely(!(mux & OCI2C_OHWR_MUX_BUSY))) if (unlikely(!(mux & OCI2C_OHWR_MUX_BUSY)))
dev_err(&adap->dev, "deselect a bus that was not selected\n"); dev_err(i2c->adap_mux->dev,
"deselect a bus that was not selected\n");
mux &= ~OCI2C_OHWR_MUX_BUSY; mux &= ~OCI2C_OHWR_MUX_BUSY;
oc_setreg(i2c, OCI2C_OHWR_MUX, mux); oc_setreg(i2c, OCI2C_OHWR_MUX, mux);
return 0; return 0;
} }
/**
* It selects the I2C bus to use and lock it
*/
static int ocores_i2c_mux_select(struct i2c_mux_core *muxc, u32 num)
{
struct ocores_i2c *i2c = muxc->priv;
return ohwr_i2c_mux_select(i2c, num);
}
/**
* It unlocks the bus so that it can be changed.
*/
static int ocores_i2c_mux_deselect(struct i2c_mux_core *muxc, u32 num)
{
struct ocores_i2c *i2c = muxc->priv;
return ohwr_i2c_mux_deselect(i2c, num);
}
/** /**
* Add OHWR multiplexer * Add OHWR multiplexer
*/ */
static int ocores_i2c_probe_ohwr(struct ocores_i2c *i2c) static int ocores_i2c_probe_ohwr(struct ocores_i2c *i2c)
{ {
int err, i; int i, err = 0;
i2c->n_adap_mux = 2; i2c->adap_mux = i2c_mux_alloc(&i2c->adap, i2c->adap.dev.parent,
i2c->adap_mux = devm_kzalloc(&i2c->adap.dev, 2, sizeof(i2c), 0,
sizeof(void *) * i2c->n_adap_mux, ocores_i2c_mux_select,
GFP_KERNEL); ocores_i2c_mux_deselect);
for (i = 0; i < i2c->n_adap_mux; ++i) { if (!i2c->adap_mux) {
i2c->adap_mux[i] = i2c_add_mux_adapter(&i2c->adap, err = -ENOMEM;
i2c->adap.dev.parent, goto err_exit;
i2c, }
0, i, i2c->adap_mux->priv = i2c;
#if KERNEL_VERSION(3, 7, 0) <= LINUX_VERSION_CODE for (i = 0; i < i2c->adap_mux->max_adapters; ++i) {
0, /* class */ err = i2c_mux_add_adapter(i2c->adap_mux,
#endif 0, i, 0);
ocores_i2c_mux_select, if (err)
ocores_i2c_mux_deselect);
if (!i2c->adap_mux[i])
goto err_add; goto err_add;
} }
return 0; return 0;
err_add: err_add:
while (--i >= 0) i2c_mux_del_adapters(i2c->adap_mux);
i2c_del_mux_adapter(i2c->adap_mux[i]); err_exit:
return err; return err;
} }
...@@ -660,10 +750,7 @@ err_add: ...@@ -660,10 +750,7 @@ err_add:
*/ */
static void ocores_i2c_remove_ohwr(struct ocores_i2c *i2c) static void ocores_i2c_remove_ohwr(struct ocores_i2c *i2c)
{ {
int i; i2c_mux_del_adapters(i2c->adap_mux);
for (i = 0; i < i2c->n_adap_mux; ++i)
i2c_del_mux_adapter(i2c->adap_mux[i]);
} }
#ifdef CONFIG_OF #ifdef CONFIG_OF
......
[*]
[*] GTKWave Analyzer v3.3.98 (w)1999-2018 BSI
[*] Tue Mar 3 12:42:41 2020
[*]
[dumpfile] "/home/tgingold/Repositories/ohwr/general-cores/modules/wishbone/wb16_to_wb32/tb/tb.ghw"
[dumpfile_mtime] "Tue Mar 3 12:40:34 2020"
[dumpfile_size] 2363
[savefile] "/home/tgingold/Repositories/ohwr/general-cores/modules/wishbone/wb16_to_wb32/tb/tb.gtkw"
[timestart] 0
[size] 1620 1056
[pos] -1 -1
*-23.629396 61240000 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
[treeopen] top.
[treeopen] top.tb_wb16_to_wb32.
[sst_width] 283
[signals_width] 102
[sst_expanded] 1
[sst_vpaned_height] 315
@28
top.tb_wb16_to_wb32.rst_n
top.tb_wb16_to_wb32.clk
@200
-wb32
@28
top.tb_wb16_to_wb32.wb32_in.cyc
top.tb_wb16_to_wb32.wb32_in.stb
top.tb_wb16_to_wb32.wb32_in.we
top.tb_wb16_to_wb32.wb32_out.ack
@22
#{top.tb_wb16_to_wb32.wb32_out.dat[31:0]} top.tb_wb16_to_wb32.wb32_out.dat[31] top.tb_wb16_to_wb32.wb32_out.dat[30] top.tb_wb16_to_wb32.wb32_out.dat[29] top.tb_wb16_to_wb32.wb32_out.dat[28] top.tb_wb16_to_wb32.wb32_out.dat[27] top.tb_wb16_to_wb32.wb32_out.dat[26] top.tb_wb16_to_wb32.wb32_out.dat[25] top.tb_wb16_to_wb32.wb32_out.dat[24] top.tb_wb16_to_wb32.wb32_out.dat[23] top.tb_wb16_to_wb32.wb32_out.dat[22] top.tb_wb16_to_wb32.wb32_out.dat[21] top.tb_wb16_to_wb32.wb32_out.dat[20] top.tb_wb16_to_wb32.wb32_out.dat[19] top.tb_wb16_to_wb32.wb32_out.dat[18] top.tb_wb16_to_wb32.wb32_out.dat[17] top.tb_wb16_to_wb32.wb32_out.dat[16] top.tb_wb16_to_wb32.wb32_out.dat[15] top.tb_wb16_to_wb32.wb32_out.dat[14] top.tb_wb16_to_wb32.wb32_out.dat[13] top.tb_wb16_to_wb32.wb32_out.dat[12] top.tb_wb16_to_wb32.wb32_out.dat[11] top.tb_wb16_to_wb32.wb32_out.dat[10] top.tb_wb16_to_wb32.wb32_out.dat[9] top.tb_wb16_to_wb32.wb32_out.dat[8] top.tb_wb16_to_wb32.wb32_out.dat[7] top.tb_wb16_to_wb32.wb32_out.dat[6] top.tb_wb16_to_wb32.wb32_out.dat[5] top.tb_wb16_to_wb32.wb32_out.dat[4] top.tb_wb16_to_wb32.wb32_out.dat[3] top.tb_wb16_to_wb32.wb32_out.dat[2] top.tb_wb16_to_wb32.wb32_out.dat[1] top.tb_wb16_to_wb32.wb32_out.dat[0]
#{top.tb_wb16_to_wb32.wb32_in.adr[31:0]} top.tb_wb16_to_wb32.wb32_in.adr[31] top.tb_wb16_to_wb32.wb32_in.adr[30] top.tb_wb16_to_wb32.wb32_in.adr[29] top.tb_wb16_to_wb32.wb32_in.adr[28] top.tb_wb16_to_wb32.wb32_in.adr[27] top.tb_wb16_to_wb32.wb32_in.adr[26] top.tb_wb16_to_wb32.wb32_in.adr[25] top.tb_wb16_to_wb32.wb32_in.adr[24] top.tb_wb16_to_wb32.wb32_in.adr[23] top.tb_wb16_to_wb32.wb32_in.adr[22] top.tb_wb16_to_wb32.wb32_in.adr[21] top.tb_wb16_to_wb32.wb32_in.adr[20] top.tb_wb16_to_wb32.wb32_in.adr[19] top.tb_wb16_to_wb32.wb32_in.adr[18] top.tb_wb16_to_wb32.wb32_in.adr[17] top.tb_wb16_to_wb32.wb32_in.adr[16] top.tb_wb16_to_wb32.wb32_in.adr[15] top.tb_wb16_to_wb32.wb32_in.adr[14] top.tb_wb16_to_wb32.wb32_in.adr[13] top.tb_wb16_to_wb32.wb32_in.adr[12] top.tb_wb16_to_wb32.wb32_in.adr[11] top.tb_wb16_to_wb32.wb32_in.adr[10] top.tb_wb16_to_wb32.wb32_in.adr[9] top.tb_wb16_to_wb32.wb32_in.adr[8] top.tb_wb16_to_wb32.wb32_in.adr[7] top.tb_wb16_to_wb32.wb32_in.adr[6] top.tb_wb16_to_wb32.wb32_in.adr[5] top.tb_wb16_to_wb32.wb32_in.adr[4] top.tb_wb16_to_wb32.wb32_in.adr[3] top.tb_wb16_to_wb32.wb32_in.adr[2] top.tb_wb16_to_wb32.wb32_in.adr[1] top.tb_wb16_to_wb32.wb32_in.adr[0]
#{top.tb_wb16_to_wb32.wb32_in.dat[31:0]} top.tb_wb16_to_wb32.wb32_in.dat[31] top.tb_wb16_to_wb32.wb32_in.dat[30] top.tb_wb16_to_wb32.wb32_in.dat[29] top.tb_wb16_to_wb32.wb32_in.dat[28] top.tb_wb16_to_wb32.wb32_in.dat[27] top.tb_wb16_to_wb32.wb32_in.dat[26] top.tb_wb16_to_wb32.wb32_in.dat[25] top.tb_wb16_to_wb32.wb32_in.dat[24] top.tb_wb16_to_wb32.wb32_in.dat[23] top.tb_wb16_to_wb32.wb32_in.dat[22] top.tb_wb16_to_wb32.wb32_in.dat[21] top.tb_wb16_to_wb32.wb32_in.dat[20] top.tb_wb16_to_wb32.wb32_in.dat[19] top.tb_wb16_to_wb32.wb32_in.dat[18] top.tb_wb16_to_wb32.wb32_in.dat[17] top.tb_wb16_to_wb32.wb32_in.dat[16] top.tb_wb16_to_wb32.wb32_in.dat[15] top.tb_wb16_to_wb32.wb32_in.dat[14] top.tb_wb16_to_wb32.wb32_in.dat[13] top.tb_wb16_to_wb32.wb32_in.dat[12] top.tb_wb16_to_wb32.wb32_in.dat[11] top.tb_wb16_to_wb32.wb32_in.dat[10] top.tb_wb16_to_wb32.wb32_in.dat[9] top.tb_wb16_to_wb32.wb32_in.dat[8] top.tb_wb16_to_wb32.wb32_in.dat[7] top.tb_wb16_to_wb32.wb32_in.dat[6] top.tb_wb16_to_wb32.wb32_in.dat[5] top.tb_wb16_to_wb32.wb32_in.dat[4] top.tb_wb16_to_wb32.wb32_in.dat[3] top.tb_wb16_to_wb32.wb32_in.dat[2] top.tb_wb16_to_wb32.wb32_in.dat[1] top.tb_wb16_to_wb32.wb32_in.dat[0]
#{top.tb_wb16_to_wb32.wb32_in.sel[3:0]} top.tb_wb16_to_wb32.wb32_in.sel[3] top.tb_wb16_to_wb32.wb32_in.sel[2] top.tb_wb16_to_wb32.wb32_in.sel[1] top.tb_wb16_to_wb32.wb32_in.sel[0]
@200
-wb16
@28
top.tb_wb16_to_wb32.wb16_out.cyc
top.tb_wb16_to_wb32.wb16_out.stb
top.tb_wb16_to_wb32.wb16_out.we
@29
top.tb_wb16_to_wb32.wb16_in.stall
@22
#{top.tb_wb16_to_wb32.wb16_out.adr[31:0]} top.tb_wb16_to_wb32.wb16_out.adr[31] top.tb_wb16_to_wb32.wb16_out.adr[30] top.tb_wb16_to_wb32.wb16_out.adr[29] top.tb_wb16_to_wb32.wb16_out.adr[28] top.tb_wb16_to_wb32.wb16_out.adr[27] top.tb_wb16_to_wb32.wb16_out.adr[26] top.tb_wb16_to_wb32.wb16_out.adr[25] top.tb_wb16_to_wb32.wb16_out.adr[24] top.tb_wb16_to_wb32.wb16_out.adr[23] top.tb_wb16_to_wb32.wb16_out.adr[22] top.tb_wb16_to_wb32.wb16_out.adr[21] top.tb_wb16_to_wb32.wb16_out.adr[20] top.tb_wb16_to_wb32.wb16_out.adr[19] top.tb_wb16_to_wb32.wb16_out.adr[18] top.tb_wb16_to_wb32.wb16_out.adr[17] top.tb_wb16_to_wb32.wb16_out.adr[16] top.tb_wb16_to_wb32.wb16_out.adr[15] top.tb_wb16_to_wb32.wb16_out.adr[14] top.tb_wb16_to_wb32.wb16_out.adr[13] top.tb_wb16_to_wb32.wb16_out.adr[12] top.tb_wb16_to_wb32.wb16_out.adr[11] top.tb_wb16_to_wb32.wb16_out.adr[10] top.tb_wb16_to_wb32.wb16_out.adr[9] top.tb_wb16_to_wb32.wb16_out.adr[8] top.tb_wb16_to_wb32.wb16_out.adr[7] top.tb_wb16_to_wb32.wb16_out.adr[6] top.tb_wb16_to_wb32.wb16_out.adr[5] top.tb_wb16_to_wb32.wb16_out.adr[4] top.tb_wb16_to_wb32.wb16_out.adr[3] top.tb_wb16_to_wb32.wb16_out.adr[2] top.tb_wb16_to_wb32.wb16_out.adr[1] top.tb_wb16_to_wb32.wb16_out.adr[0]
#{top.tb_wb16_to_wb32.wb16_out.dat[31:0]} top.tb_wb16_to_wb32.wb16_out.dat[31] top.tb_wb16_to_wb32.wb16_out.dat[30] top.tb_wb16_to_wb32.wb16_out.dat[29] top.tb_wb16_to_wb32.wb16_out.dat[28] top.tb_wb16_to_wb32.wb16_out.dat[27] top.tb_wb16_to_wb32.wb16_out.dat[26] top.tb_wb16_to_wb32.wb16_out.dat[25] top.tb_wb16_to_wb32.wb16_out.dat[24] top.tb_wb16_to_wb32.wb16_out.dat[23] top.tb_wb16_to_wb32.wb16_out.dat[22] top.tb_wb16_to_wb32.wb16_out.dat[21] top.tb_wb16_to_wb32.wb16_out.dat[20] top.tb_wb16_to_wb32.wb16_out.dat[19] top.tb_wb16_to_wb32.wb16_out.dat[18] top.tb_wb16_to_wb32.wb16_out.dat[17] top.tb_wb16_to_wb32.wb16_out.dat[16] top.tb_wb16_to_wb32.wb16_out.dat[15] top.tb_wb16_to_wb32.wb16_out.dat[14] top.tb_wb16_to_wb32.wb16_out.dat[13] top.tb_wb16_to_wb32.wb16_out.dat[12] top.tb_wb16_to_wb32.wb16_out.dat[11] top.tb_wb16_to_wb32.wb16_out.dat[10] top.tb_wb16_to_wb32.wb16_out.dat[9] top.tb_wb16_to_wb32.wb16_out.dat[8] top.tb_wb16_to_wb32.wb16_out.dat[7] top.tb_wb16_to_wb32.wb16_out.dat[6] top.tb_wb16_to_wb32.wb16_out.dat[5] top.tb_wb16_to_wb32.wb16_out.dat[4] top.tb_wb16_to_wb32.wb16_out.dat[3] top.tb_wb16_to_wb32.wb16_out.dat[2] top.tb_wb16_to_wb32.wb16_out.dat[1] top.tb_wb16_to_wb32.wb16_out.dat[0]
#{top.tb_wb16_to_wb32.wb16_out.sel[3:0]} top.tb_wb16_to_wb32.wb16_out.sel[3] top.tb_wb16_to_wb32.wb16_out.sel[2] top.tb_wb16_to_wb32.wb16_out.sel[1] top.tb_wb16_to_wb32.wb16_out.sel[0]
@28
top.tb_wb16_to_wb32.wb16_in.ack
@22
#{top.tb_wb16_to_wb32.wb16_in.dat[31:0]} top.tb_wb16_to_wb32.wb16_in.dat[31] top.tb_wb16_to_wb32.wb16_in.dat[30] top.tb_wb16_to_wb32.wb16_in.dat[29] top.tb_wb16_to_wb32.wb16_in.dat[28] top.tb_wb16_to_wb32.wb16_in.dat[27] top.tb_wb16_to_wb32.wb16_in.dat[26] top.tb_wb16_to_wb32.wb16_in.dat[25] top.tb_wb16_to_wb32.wb16_in.dat[24] top.tb_wb16_to_wb32.wb16_in.dat[23] top.tb_wb16_to_wb32.wb16_in.dat[22] top.tb_wb16_to_wb32.wb16_in.dat[21] top.tb_wb16_to_wb32.wb16_in.dat[20] top.tb_wb16_to_wb32.wb16_in.dat[19] top.tb_wb16_to_wb32.wb16_in.dat[18] top.tb_wb16_to_wb32.wb16_in.dat[17] top.tb_wb16_to_wb32.wb16_in.dat[16] top.tb_wb16_to_wb32.wb16_in.dat[15] top.tb_wb16_to_wb32.wb16_in.dat[14] top.tb_wb16_to_wb32.wb16_in.dat[13] top.tb_wb16_to_wb32.wb16_in.dat[12] top.tb_wb16_to_wb32.wb16_in.dat[11] top.tb_wb16_to_wb32.wb16_in.dat[10] top.tb_wb16_to_wb32.wb16_in.dat[9] top.tb_wb16_to_wb32.wb16_in.dat[8] top.tb_wb16_to_wb32.wb16_in.dat[7] top.tb_wb16_to_wb32.wb16_in.dat[6] top.tb_wb16_to_wb32.wb16_in.dat[5] top.tb_wb16_to_wb32.wb16_in.dat[4] top.tb_wb16_to_wb32.wb16_in.dat[3] top.tb_wb16_to_wb32.wb16_in.dat[2] top.tb_wb16_to_wb32.wb16_in.dat[1] top.tb_wb16_to_wb32.wb16_in.dat[0]
[pattern_trace] 1
[pattern_trace] 0
library ieee;
use ieee.std_logic_1164.all;
use work.wishbone_pkg.all;
entity tb_wb16_to_wb32 is
end;
architecture behav of tb_wb16_to_wb32 is
signal clk : std_logic;
signal rst_n : std_logic;
signal wb16_in : t_wishbone_master_in;
signal wb16_out : t_wishbone_master_out;
signal wb32_in : t_wishbone_slave_in;
signal wb32_out : t_wishbone_slave_out;
signal reg1 : std_logic_vector(31 downto 0);
signal reg0 : std_logic_vector(31 downto 0);
signal done : boolean := false;
begin
dut: entity work.wb16_to_wb32
port map (
clk_i => clk,
rst_n_i => rst_n,
wb16_i => wb16_out,
wb16_o => wb16_in,
wb32_i => wb32_out,
wb32_o => wb32_in
);
process
begin
clk <= '0';
wait for 5 ns;
clk <= '1';
wait for 5 ns;
if done then
wait;
end if;
end process;
-- Simple slave with 2 registers.
process (clk)
begin
if rising_edge(clk) then
if rst_n = '0' then
reg1 <= x"4400_3300";
reg0 <= x"2200_1100";
wb32_out.ack <= '0';
else
if wb32_in.cyc = '1' and wb32_in.stb = '1' then
if wb32_in.we = '1' then
if wb32_in.adr (2) = '1' then
reg1 <= wb32_in.dat;
else
reg0 <= wb32_in.dat;
end if;
else
if wb32_in.adr (2) = '1' then
wb32_out.dat <= reg1;
else
wb32_out.dat <= reg0;
end if;
end if;
wb32_out.ack <= '1';
else
wb32_out.ack <= '0';
end if;
end if;
end if;
end process;
process
procedure wait_ack is
begin
loop
wait until rising_edge (clk);
exit when wb16_in.ack = '1';
end loop;
end wait_ack;
procedure read16 (addr : std_logic_vector (31 downto 0)) is
begin
wb16_out.adr <= addr;
wb16_out.we <= '0';
wb16_out.cyc <= '1';
wb16_out.stb <= '1';
wait_ack;
end read16;
procedure write16 (addr : std_logic_vector (31 downto 0); dat : std_logic_vector(15 downto 0)) is
begin
wb16_out.adr <= addr;
wb16_out.dat (15 downto 0) <= dat;
wb16_out.we <= '1';
wb16_out.sel <= "0011";
wb16_out.cyc <= '1';
wb16_out.stb <= '1';
wait_ack;
end write16;
begin
rst_n <= '0';
wait until rising_edge (clk);
wait until rising_edge (clk);
rst_n <= '1';
read16 (x"0000_0000");
assert wb16_in.dat (15 downto 0) = x"1100" severity failure;
read16 (x"0000_0002");
assert wb16_in.dat (15 downto 0) = x"2200" severity failure;
read16 (x"0000_0004");
assert wb16_in.dat (15 downto 0) = x"3300" severity failure;
read16 (x"0000_0006");
assert wb16_in.dat (15 downto 0) = x"4400" severity failure;
write16(x"0000_0002", x"0220");
write16(x"0000_0000", x"0110");
read16 (x"0000_0000");
assert wb16_in.dat (15 downto 0) = x"0110" severity failure;
read16 (x"0000_0002");
assert wb16_in.dat (15 downto 0) = x"0220" severity failure;
done <= true;
report "done";
wait;
end process;
end behav;
\ No newline at end of file
...@@ -3,10 +3,16 @@ ...@@ -3,10 +3,16 @@
with open("buildinfo_pkg.vhd", "w") as f: with open("buildinfo_pkg.vhd", "w") as f:
import subprocess import subprocess
import time import time
commitid = subprocess.check_output( try:
commitid = subprocess.check_output(
["git", "log", "-1", "--format=%H"]).decode().strip() ["git", "log", "-1", "--format=%H"]).decode().strip()
userid = subprocess.check_output( except:
commitid = "unknown"
try:
userid = subprocess.check_output(
["git", "config", "--get", "user.name"]).decode().strip() ["git", "config", "--get", "user.name"]).decode().strip()
except:
userid = "unknown"
if action == "simulation": if action == "simulation":
top = sim_top top = sim_top
tool = sim_tool tool = sim_tool
......
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