Skip to content

Commit

Permalink
FEATURE: Add Sample Rate Converter simple (#63)
Browse files Browse the repository at this point in the history
* DEVEL: SRC up and down simple with doc- first commit

* BUGFIX: change rstPol into src

* BUGFIX: change rstPol and vld

* DOC: MD file title name change

* DOC: change link from top README.md

* DOC: change logo psi_logo.png
  • Loading branch information
BenoitStef authored Sep 16, 2024
1 parent bbed262 commit 9c39e3d
Show file tree
Hide file tree
Showing 8 changed files with 297 additions and 2 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Benoît Stef [[email protected]]
This library is published under [PSI HDL Library License](License.txt), which is [LGPL](LGPL2_1.txt) plus some additional exceptions to clarify the LGPL terms in the context of firmware development.

## Detailed Documentation
See [Documentation](doc/psi_common_index.md)
See [Documentation](doc/README.md)

## Changelog
See [Changelog](Changelog.md)
Expand Down
3 changes: 2 additions & 1 deletion doc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,8 @@ Serial to parallel | [psi_common_ser_par.vhd](../hdl/psi_common_ser_par.vhd
Find Min Max | [psi_common_find_min_max.vhd](../hdl/psi_common_find_min_max.vhd) | [link](files/psi_common_find_min_max.md)
Min Max Sum | [psi_common_find_min_max.vhd](../hdl/psi_common_min_max_sum.vhd) | [link](files/psi_common_min_max_sum.md)
PRBS | [psi_common_prbs.vhd](../hdl/psi_common_prbs.vhd) | [link](files/psi_common_prbs.md)
PWM | [psi_common_pwm.vhd](../hdl/psi_common_pwm.vhd) | [link](files/psi_common_pwm.md) |
PWM | [psi_common_pwm.vhd](../hdl/psi_common_pwm.vhd) | [link](files/psi_common_pwm.md)
Sample Rate Converter - no filter | [psi_common_sample_rate_converter.vhd](../hdl/psi_common_sample_rate_converter.vhd) | [link](files/psi_common_sample_rate_converter.md)
***

### Packages
Expand Down
39 changes: 39 additions & 0 deletions doc/files/psi_common_sample_rate_converter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<img align="right" src="../psi_logo.png">

***

[**component list**](../README.md)

# psi_common_sample_rate_converter
- VHDL source: [psi_common_sample_rate_converter.vhd](../../hdl/psi_common_sample_rate_converter.vhd)
- Testbench source: [psi_common_sample_rate_converter_tb.vhd](../../testbench/psi_common_sample_rate_converter_tb/psi_common_sample_rate_converter_tb.vhd)

### Description

This block is converting the valid input strobe signal to a desired rate without any filter, the same block be mean of generic can produce over sampling signal output
or reduce sampling rate. For downsampling a pre filter is recommended and opposite for up sampling mode a post filter to smooth or interpolate is also advised.

<p align="center"><img src="psi_common_sample_rate_converter.png"></p>


### Generics
| Name | type | Description |
|:--------------------|:----------|:----------------------------------------------------|
| RATE_g | integer | Sampling rate factor for up/down sampling |
| MODE_g | real | "DOWN" => downsampling, "UP" => upsampling |
| length_g | positive | data input length in bit |
| CLK_TO_VALID_RATIO_g| positive | Ratio between clock frequency and vld input frequency|
| rst_pol_g | std_logic | polarity reset |

### Interfaces
| Name | In/Out | Length | Description |
|:--------|:---------|:--------------------|:----------------|
| clk_i | i | 1 | clock |
| rst_i | i | 1 | reset |
| dat_i | i | length_g | input data |
| vld_i | i | 1 | valid in |
| dat_o | o | length_g | data output |
| vld_o | o | 1 | valid output |


[**component list**](../README.md)
Binary file added doc/files/psi_common_sample_rate_converter.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified doc/psi_logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
93 changes: 93 additions & 0 deletions hdl/psi_common_sample_rate_converter.vhd
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
------------------------------------------------------------------------------
-- Copyright (c) 2024 by Paul Scherrer Institute, Switzerland
-- All rights reserved.
-- Authors: Benoit Stef
------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
-- @formatter:off
entity psi_common_sample_rate_converter is
generic(rate_g : integer := 2; -- Sampling rate factor for up/down sampling
mode_g : string := "DOWN"; -- "DOWN" => downsampling, "UP" => upsampling
length_g : natural := 16; -- nb of bit
clk_to_vld_ratio_g : integer := 10; -- Ratio between clock frequency and vld input frequency
rst_pol_g : std_logic := '1');
port( clk_i : in std_logic; -- Clock
rst_i : in std_logic; -- Synchronous rst
vld_i : in std_logic; -- Indicates when dat_i is vld
dat_i : in std_logic_vector(length_g - 1 downto 0); -- Assuming 16-bit data in
dat_o : out std_logic_vector(length_g - 1 downto 0); -- Data output align with vld out
vld_o : out std_logic); -- Indicates when dat_o is vld
end entity;
-- @formatter:on
architecture rtl of psi_common_sample_rate_converter is
signal sample_count_s : integer := 0;
signal dat_s : std_logic_vector(length_g - 1 downto 0);
signal vld_s : std_logic := '0';
signal sample_s : std_logic_vector(length_g - 1 downto 0);
signal vld_out_count_s : integer := 0;
signal vdl_dff_s : std_logic := '0';
begin

proc : process(clk_i)
begin
if rising_edge(clk_i) then
if rst_i = rst_pol_g then
-- Synchronous reset: reset everything on the rising edge of clk_i
sample_count_s <= 0;
dat_s <= (others => '0');
vld_s <= '0';
sample_s <= (others => '0');
vdl_dff_s <= '0';
else
if mode_g = "DOWN" then
if vld_i = '1' then
-- Downsampling logic
if sample_count_s = (rate_g - 1) then
dat_s <= dat_i;
vld_s <= '1';
sample_count_s <= 0;
else
vld_s <= '0';
sample_count_s <= sample_count_s + 1;
end if;
else
-- No valid input, no valid output
vld_s <= '0';
end if;

elsif mode_g = "UP" then
-- Check for valid input transition
if clk_to_vld_ratio_g = rate_g then
dat_s <= dat_i;
vld_s <= '1';
else
if vld_i = '1' and vdl_dff_s = '0' then
-- Store the current input sample and initialize counters
sample_s <= dat_i;
vld_out_count_s <= clk_to_vld_ratio_g / rate_g;
end if;

vdl_dff_s <= vld_i;
if vld_out_count_s > 0 then
-- Assert valid output
dat_s <= sample_s;
vld_s <= '0';
vld_out_count_s <= vld_out_count_s - 1;
elsif vld_out_count_s = 0 then
vld_out_count_s <= clk_to_vld_ratio_g / rate_g;
dat_s <= sample_s;
vld_s <= '1';
end if;
end if;
end if;

end if;
end if;
end process;

dat_o <= dat_s;
vld_o <= vld_s;

end architecture;
2 changes: 2 additions & 0 deletions sim/config.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ add_sources "../hdl" {
psi_common_min_max_sum.vhd \
psi_common_prbs.vhd \
psi_common_pwm.vhd \
psi_common_sample_rate_converter.vhd \
} -tag src

# testbenches
Expand Down Expand Up @@ -158,6 +159,7 @@ add_sources "../testbench" {
psi_common_prbs_tb/psi_common_prbs_tb.vhd \
psi_common_pwm_tb/psi_common_pwm_tb.vhd \
psi_common_pulse_cc_tb/psi_common_pulse_cc_tb.vhd \
psi_common_sample_rate_converter_tb/psi_common_sample_rate_converter_tb.vhd \
} -tag tb

#TB Runs
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.MATH_REAL.ALL;

entity psi_common_sample_rate_converter_tb is
end entity psi_common_sample_rate_converter_tb;

architecture tb of psi_common_sample_rate_converter_tb is

-- Constants
constant CLK_PERIOD_c : time := 4 ns; -- 250 MHz clock period
constant SAMPLE_RATE_c : real := 250.0e6; -- 250 MHz sample rate
constant SIGNAL_FREQ_c : real := 1.0e6; -- 1 MHz sine wave frequency
constant DOWNSAMPLE_RATE_c : integer := 512; -- Downsampling rate
constant UP_SAMPLING_RATE_c : integer := 8;
constant NUM_INPUT_SAMPLES_c : integer := 10000; -- Number of input samples
constant LENGTH_c : integer := 16; -- slv size
-- Testbench signals
signal clk_i : std_logic := '0';
signal rst_i : std_logic := '0';
signal vld_i : std_logic := '0';
signal dat_i : std_logic_vector(LENGTH_c - 1 downto 0) := (others => '0');
signal dat_o : std_logic_vector(LENGTH_c - 1 downto 0) := (others => 'L');
signal vld_o : std_logic := 'L';

-- Variables for sine wave generation and self-test
signal sine_wave : real := 0.0;
signal sample_count : integer := 0;
signal valid_count : integer := 0; -- To count valid input samples
signal validup_count : integer := 0;
signal expected_count : integer := 0;
signal count_ok : boolean := true;
signal countup_ok : boolean := true;
signal datup_o : std_logic_vector(LENGTH_c - 1 downto 0);
signal vldup_o : std_logic;
signal tb_run : boolean := true;
begin

-- Clock generation process
clk_proc : process
begin
while tb_run loop
clk_i <= '0';
wait for CLK_PERIOD_c / 2;
clk_i <= '1';
wait for CLK_PERIOD_c / 2;
end loop;
wait;
end process;

-- Sine wave generation and signal application
stim_proc : process
variable t : real := 0.0; -- Time variable in seconds
begin
rst_i <= '1';
wait for 10 * CLK_PERIOD_c;
rst_i <= '0';

vld_i <= '1';
wait for CLK_PERIOD_c;

while sample_count < 10000 loop -- Generate 10000 samples
t := real(sample_count + 1) / SAMPLE_RATE_c;
sine_wave <= sin(2.0 * math_pi * SIGNAL_FREQ_c * t);
dat_i <= std_logic_vector(to_signed(integer(2.0**(LENGTH_c - 1) * sine_wave), LENGTH_c)); -- 16-bit signed output
wait for CLK_PERIOD_c;
sample_count <= sample_count + 1;
end loop;

-- Finish simulation
wait for 100 * CLK_PERIOD_c;
tb_run <= false;
report "###INFO: End of simulation";
wait;
end process;

-- Instantiate the SampleRateConverter
dut_dw_inst : entity work.psi_common_sample_rate_converter
generic map(
RATE_g => DOWNSAMPLE_RATE_c, -- Downsampling rate
MODE_g => "DOWN", -- Downsampling mode
length_g => LENGTH_c,
clk_to_vld_ratio_g => 1)
port map(
clk_i => clk_i,
rst_i => rst_i,
vld_i => vld_i,
dat_i => dat_i,
dat_o => dat_o,
vld_o => vld_o
);

dut_up_inst : entity work.psi_common_sample_rate_converter
generic map(
RATE_g => UP_SAMPLING_RATE_c, -- UP sample rate
MODE_g => "UP", --Upsampling mode
length_g => LENGTH_c,
clk_to_vld_ratio_g => DOWNSAMPLE_RATE_c
)
port map(
clk_i => clk_i,
rst_i => rst_i,
vld_i => vld_o,
dat_i => dat_o,
dat_o => datup_o,
vld_o => vldup_o
);

-- Count the number of valid input samples between valid output samples
count_valid_dw_proc : process(clk_i)
begin
if rising_edge(clk_i) then
assert count_ok report "###ERROR### :Test DW Failed: Number of valid input samples between valid outputs does not match the downsampling ratio." severity error;
if rst_i = '1' then
valid_count <= 0;
count_ok <= true; -- Reset status
elsif vld_i = '1' then
valid_count <= valid_count + 1; -- Count valid input samples
if vld_o = '1' then
-- When a valid output is produced, check the count of valid inputs
if valid_count < (DOWNSAMPLE_RATE_c - 1) or valid_count > (DOWNSAMPLE_RATE_c + 1) then
count_ok <= false; -- Flag an error if the count does not match
else
count_ok <= true;
end if;
valid_count <= 0; -- Reset count for the next segment
end if;
end if;
end if;
end process;

-- Count the number of valid input samples between valid output samples
count_valid_up_proc : process(clk_i)
begin
if rising_edge(clk_i) then
assert countup_ok report "###ERROR### :Test UP Failed: Number of valid input samples between valid outputs does not match the upsampling ratio." severity error;
if rst_i = '1' then
validup_count <= 0;
-- Reset status
countup_ok <= true;
elsif vld_o = '1' then
-- Count valid input samples
validup_count <= validup_count + 1;
if vldup_o = '1' then
-- When a valid output is produced, check the count of valid inputs
if validup_count < (UP_SAMPLING_RATE_c-1) or validup_count > (UP_SAMPLING_RATE_c+1) then
-- Flag an error if the count does not match
countup_ok <= false;
else
countup_ok <= true;
end if;
-- Reset count for the next segment
validup_count <= 0;
end if;
end if;
end if;
end process;

end architecture;

0 comments on commit 9c39e3d

Please sign in to comment.