From c537fbbdbcc0b8dea0d05a1042c4d72cba3d9c50 Mon Sep 17 00:00:00 2001 From: valerixb Date: Wed, 10 Apr 2024 13:16:52 +0200 Subject: [PATCH] added PID and sine wave generator modules, as external model composer IPs --- CONFIG.maxiv | 46 +++++ apps/PandABox-maxiv-test-fmc_acq427.app.ini | 70 +++++++ ip_defs/pidmc_0.tcl | 6 + ip_defs/singenmc_0.tcl | 6 + modules/pid/extensions/pid.py | 198 ++++++++++++++++++++ modules/pid/hdl/pid.vhd | 168 +++++++++++++++++ modules/pid/pid.block.ini | 110 +++++++++++ modules/pid/pid_doc.rst | 37 ++++ modules/singen/hdl/singen.vhd | 122 ++++++++++++ modules/singen/singen.block.ini | 27 +++ modules/singen/singen_doc.rst | 29 +++ 11 files changed, 819 insertions(+) create mode 100644 CONFIG.maxiv create mode 100755 apps/PandABox-maxiv-test-fmc_acq427.app.ini create mode 100644 ip_defs/pidmc_0.tcl create mode 100644 ip_defs/singenmc_0.tcl create mode 100755 modules/pid/extensions/pid.py create mode 100644 modules/pid/hdl/pid.vhd create mode 100644 modules/pid/pid.block.ini create mode 100644 modules/pid/pid_doc.rst create mode 100755 modules/singen/hdl/singen.vhd create mode 100755 modules/singen/singen.block.ini create mode 100755 modules/singen/singen_doc.rst diff --git a/CONFIG.maxiv b/CONFIG.maxiv new file mode 100644 index 000000000..9c23ca9c4 --- /dev/null +++ b/CONFIG.maxiv @@ -0,0 +1,46 @@ +# Example configuration file for building PandA firmware, and for simulation +# +# Copy this file to a file named CONFIG and edit as appropriate. Lines that are +# commented out can be left as they are, the default value is shown. Uncommented +# lines must be assigned values. + +# Default build location. Default is to build in build subdirectory. +#BUILD_DIR = /build + +# Development Tool Version +# VIVADO_VER = 2023.2 + +# Definitions needed for FPGA build +export VIVADO = /tools/Xilinx/Vivado/\$(VIVADO_VER)/settings64.sh +# Path to external IP repository (if required) +#export EXT_IP_REPO = /xilinxworks/pandabox/PICXO_FRACXO_v40_rev6 +export EXT_IP_REPO = "{ /xilinxworks/pandabox/PICXO_FRACXO_v40_rev6 /xilinxworks/pandabox/Panda-PID/ip /xilinxworks/pandabox/Panda-singen/ip }" + +# Specifiy licence server, if required +# export LM_LICENSE_FILE = + +# Location of rootfs builder. This needs to be at least version 1.13 and can be +# downloaded from https://github.com/araneidae/rootfs +export ROOTFS_TOP = /xilinxworks/pandabox/diamond-rootfs-builder-master + +# Where to find source files +export TAR_FILES = /xilinxworks/pandabox/diamond-rootfs-builder-master/tar-files + +# Path to root filesystem +PANDA_ROOTFS = /xilinxworks/pandabox/PandABlocks-rootfs +# MAKE_ZPKG = $(PANDA_ROOTFS)/make-zpkg + +# Python interpreter for running scripts +# +# PYTHON = python3 + +# Sphinx build for documentation. +# SPHINX_BUILD = sphinx-build + +# List of default targets to build when running make +# DEFAULT_TARGETS = zpkg + +# FPGA Application Name +APP_NAME = PandABox-maxiv-test-fmc_acq427 + +# vim: set filetype=make: diff --git a/apps/PandABox-maxiv-test-fmc_acq427.app.ini b/apps/PandABox-maxiv-test-fmc_acq427.app.ini new file mode 100755 index 000000000..7ec9d3fa8 --- /dev/null +++ b/apps/PandABox-maxiv-test-fmc_acq427.app.ini @@ -0,0 +1,70 @@ +; +; app file for MaxIV deployment - FMC ACQ427 card +; comment lines begin with a semicolon +; comments at end of lines are not supported; +; only whole lines can be comments +; + +[.] +description: + MaxIV Standard Apps for PandaBox + - 10 Pulse blocks + - 3 BITS blocks + - 2 FILTER blocks + - 9 LUT + - FMC T-ACQ 427 Analog card + - do not include standard file "common_soft_blocks.include.ini", + as we want to have more pulse blocks +target: PandABox +options: !pcap_std_dev +;includes: common_soft_blocks.include.ini + +[BITS] +number: 3 + +;[CALC] +;number: 2 + +[CLOCK] +number: 2 + +[COUNTER] +number: 8 + +[DIV] +number: 2 + +[FILTER] +number: 2 + +[LUT] +number: 9 + +[PCOMP] +number: 2 +;number: 4 + +;[PGEN] +;number: 2 + +[PULSE] +number: 10 + +[SEQ] +number: 2 + +[SRGATE] +number: 4 + +[FMC] +module: fmc_acq427 + +[SFP3_SYNC] +module: sfp_panda_sync +site : sfp 3 + +[PID] +number: 1 + +[SINGEN] +number: 1 diff --git a/ip_defs/pidmc_0.tcl b/ip_defs/pidmc_0.tcl new file mode 100644 index 000000000..074d22582 --- /dev/null +++ b/ip_defs/pidmc_0.tcl @@ -0,0 +1,6 @@ +# +# Build PID IP from model composer +# IP sources and binaries can be found at git@github.com:valerixb/Panda-PID.git +# + +create_ip -name pidmc -vendor MaxIV -library Panda_ModelComp -version 1.0 -module_name pidmc_0 -dir $BUILD_DIR/ diff --git a/ip_defs/singenmc_0.tcl b/ip_defs/singenmc_0.tcl new file mode 100644 index 000000000..4b005a5d2 --- /dev/null +++ b/ip_defs/singenmc_0.tcl @@ -0,0 +1,6 @@ +# +# Build Sine Generator IP from model composer +# IP sources and binaries can be found at git@github.com:valerixb/Panda-singen.git +# + +create_ip -name singenmc -vendor MaxIV -library Panda_ModelComp -version 1.0 -module_name singenmc_0 -dir $BUILD_DIR/ diff --git a/modules/pid/extensions/pid.py b/modules/pid/extensions/pid.py new file mode 100755 index 000000000..998e55487 --- /dev/null +++ b/modules/pid/extensions/pid.py @@ -0,0 +1,198 @@ +# +# Extension module to support PID block +# model composer; variable sampling frequency; fixed point +# +# latest rev: feb 23 2024 +# + +# fixed point parameter scaling +P_SCALE = 2**23 +P_RAWMAX = (2**31-1) +I_SCALE = 2**4 +I_RAWMAX = (2**10-1) +D_SCALE = 2**7 +D_RAWMAX = (2**12-1) +FS_MAX = 1e6 +FS_MIN = 1 +FF_SCALE = 2**10 +GI_SCALE = 2**25 +G1D_SCALE = 2**32 +G2D_SCALE = 2**7 +LOG_FNAME = '/tmp/pidlog.txt' + +WRITELOG = False + +import struct + + +def float_to_hex(f): + return hex(struct.unpack('=(fs/4.))): + ff=round(fs/5.*FF_SCALE)/FF_SCALE + pid_table[number]['F_FILTER']= ff + R=fs/ff + g1d=(2*R-1)/(2*R+1)*G1D_SCALE + g2d=gd/(2*R+1)*G2D_SCALE + v1=int(g1d) + v2=int(g2d) + if WRITELOG: + s= "PID{0:d}.RESERVED_G1D={1:d}\n".format(number+1,v1) + s= s + "PID{0:d}.RESERVED_G2D={1:d}\n".format(number+1,v2) + self.logentry(s) + return(v1,v2) + + def parse_F_FILTER(self, number, value): + fs=pid_table[number]['FS'] + ff=value/FF_SCALE + # keep f_filter below Fs/4 (for bilinear convergence) and non-zero + if ((ff==0) or (ff>=(fs/4.))): + ff=round(fs/5.*FF_SCALE)/FF_SCALE + pid_table[number]['F_FILTER']= ff + kd=pid_table[number]['KD']*D_SCALE + t=self.parse_KD(number,kd) + return t + + def parse_F_SAMPLE(self, number, value): + fs=min(FS_MAX,max(FS_MIN,value)) + pid_table[number]['FS']= fs + ki=pid_table[number]['KI']*I_SCALE + kd=pid_table[number]['KD']*D_SCALE + # return concatenation of tuples + t= self.parse_KI(number,ki) + self.parse_KD(number,kd) + return t + +pid_table=[] +default_pid_params= \ + { + 'KP' : 1.0, + 'KI' : 0.0, + 'KD' : 0.0, + 'F_FILTER': 20.0, + 'FS' : 100.0 + } +PID_Reader=PidReader() +PID_Writer=PidWriter() + +class Extension: + def __init__(self, count): + # count is the number of modules of this type that are instantiated in + # this PandABox App, as defined in the app.ini file + global pid_table + pid_table=[default_pid_params.copy() for _ in range(count)] + # note that the .copy() is needed, to avoid making n references to + # the same object (dict) + + def parse_read(self, request): + # request is the extension parameter associated to this particular block field + # block_num is the which of the "count" instances is addressed + #return lambda block_num, *args: PID_Reader.read(request, block_num, *args) + return lambda block_num: PID_Reader.read(request, block_num) + + def parse_write(self, request): + # request is the extension parameter associated to this particular block field + # block_num is the which of the "count" instances is addressed + methodName="parse_"+request.upper() + theMethod=getattr(PID_Writer,methodName) + #return lambda block_num, value, *args: theMethod(block_num, value, *args) + return lambda block_num, value: theMethod(block_num, value) + + + +# --------------- main ------------------------ +# just for test + +# if __name__ == '__main__': +# +# cicci = Extension(2) +# print(pid_table) +# kp_reader=cicci.parse_read('kP') +# kp_writer=cicci.parse_write('kP') +# ki_reader=cicci.parse_read('kI') +# ki_writer=cicci.parse_write('kI') +# kd_reader=cicci.parse_read('kD') +# kd_writer=cicci.parse_write('kD') +# ffilt_reader=cicci.parse_read('f_filter') +# ffilt_writer=cicci.parse_write('f_filter') +# # f_filt +# print('------------') +# readback_val= ffilt_reader.read(0) +# print("f_filt1 before=",readback_val) +# ffilt_writer.write(0,14756) +# readback_val= ffilt_reader.read(0) +# print("f_filt1 after=",readback_val) +# # k_p +# print('------------') +# readback_val= kp_reader.read(0) +# print("kp1 before=",readback_val) +# kp_writer.write(0,3.4*PARAM_SCALE) +# readback_val= kp_reader.read(0) +# print("kp1 after=",readback_val) +# readback_val= kp_reader.read(1) +# print("kp2 after=",readback_val) +# print('------------') +# print(pid_table) +# # use case kp=7, ki=2, kd=0.1, ff=5kHz +# print('================') +# # GP = 1088421888 +# # GI = 897988541 +# # G1D= 1065286241 +# # G2D= 1137154510 +# ffilt_writer.write(1,4000) +# kp_writer.write(1,7*PARAM_SCALE) +# ki_writer.write(1,2.0*PARAM_SCALE) +# kd_writer.write(1,0.1*PARAM_SCALE) +# print(pid_table) diff --git a/modules/pid/hdl/pid.vhd b/modules/pid/hdl/pid.vhd new file mode 100644 index 000000000..13cc149c5 --- /dev/null +++ b/modules/pid/hdl/pid.vhd @@ -0,0 +1,168 @@ +-------------------------------------------------------------------------------- +-- PandA Motion Project - 2024 +-- Diamond Light Source, Oxford, UK +-- SOLEIL Synchrotron, GIF-sur-YVETTE, France +-- MaxIV Laboratory, Lund, Sweden +-- +-------------------------------------------------------------------------------- +-- +-- Description : PID controller; +-- - developed with model composer +-- - floating point +-- - fixed sampling frequency=1 MHz +-- +-- latest rev : feb 2 2024 +-- +-------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + + +entity pid is +port ( + -- Clock and Reset + clk_i : in std_logic; + ENABLE_i : in std_logic; + -- Block Input and Outputs + cmd_i : in std_logic_vector(31 downto 0); -- sfix32_En31 + meas_i : in std_logic_vector(31 downto 0); -- sfix32_En31 + thresh : in std_logic_vector(31 downto 0); -- sfix32_En30 + anti_int_wndp_rat_g : in std_logic_vector(31 downto 0); -- sfix32_En30 + max_out : in std_logic_vector(31 downto 0); -- sfix32_En30 + out_o : out std_logic_vector(31 downto 0); -- sfix32_En31 + -- internal gains + reserved_gp : in std_logic_vector(31 downto 0); -- ufix32_En24 - note: UNSIGNED + reserved_gi : in std_logic_vector(31 downto 0); -- ufix32_En25 - note: UNSIGNED + reserved_g1d : in std_logic_vector(31 downto 0); -- ufix32_En32 - note: UNSIGNED + reserved_g2d : in std_logic_vector(31 downto 0); -- ufix32_En07 - note: UNSIGNED + -- boolean + sample_clk_i : in std_logic; + inv_cmd : in std_logic_vector(31 downto 0); + inv_meas : in std_logic_vector(31 downto 0); + deriv_on_procvar : in std_logic_vector(31 downto 0) + ); +end pid; + + +architecture rtl of pid is + + constant WAIT_STATES : natural := 4; + + signal pid_clkdiv_clr : std_logic := '1'; + signal pid_res : std_logic := '1'; + signal pid_ce_in : std_logic; + signal pid_ce_out, pid_ce_out_prev : std_logic; + signal sample_clk_prev : std_logic; + signal wait_cntr : natural range 0 to WAIT_STATES :=0; + + component pidmc_0 + port + ( + aiw_g : in std_logic_vector(31 downto 0); + ce : in std_logic_vector(0 DOWNTO 0); + g1d : in std_logic_vector(31 downto 0); + g2d : in std_logic_vector(31 downto 0); + gi : in std_logic_vector(31 downto 0); + pv_deriv : in std_logic_vector(0 downto 0); + command_in : in std_logic_vector(31 downto 0); + inv_command : in std_logic_vector(0 downto 0); + inv_meas : in std_logic_vector(0 downto 0); + kp : in std_logic_vector(30 downto 0); + meas_in : in std_logic_vector(31 downto 0); + res : in std_logic_vector(0 downto 0); + sat_limit : in std_logic_vector(31 downto 0); + thr_in : in std_logic_vector(31 downto 0); + clk : in std_logic; + clr : in std_logic; + ce_out : out std_logic_vector(0 downto 0); + control_out : out std_logic_vector(31 downto 0) + ); + end component; + + +begin + + the_pid: pidmc_0 + port map + ( + aiw_g => anti_int_wndp_rat_g, + ce(0) => pid_ce_in, + g1d => reserved_g1d, + g2d => reserved_g2d, + gi => reserved_gi, + pv_deriv(0) => deriv_on_procvar(0), + command_in => cmd_i, + inv_command(0) => inv_cmd(0), + inv_meas(0) => inv_meas(0), + res(0) => pid_res, + kp => reserved_gp(30 downto 0), + meas_in => meas_i, + sat_limit => max_out, + thr_in => thresh, + clk => clk_i, + clr => pid_clkdiv_clr, + ce_out(0) => pid_ce_out, + control_out => out_o + ); + + -- pipes to detect edges + pipes: process (clk_i) + begin + if rising_edge(clk_i) then + sample_clk_prev <= sample_clk_i; + pid_ce_out_prev <= pid_ce_out; + end if; + end process pipes; + + -- synchronize reset to the PID internal 1 MHz clock (== ce_out pulse) + reset_process : process (clk_i, ENABLE_i) + begin + if rising_edge(clk_i) then + if ENABLE_i = '0' then + pid_res <= '1'; + if (pid_clkdiv_clr = '0') and (wait_cntr=WAIT_STATES) then + pid_clkdiv_clr <= '1'; + else + pid_clkdiv_clr <= pid_clkdiv_clr; + end if; + else + pid_clkdiv_clr <= '0'; + pid_res <= '0'; + end if; + end if; + end process reset_process; + + -- wait until the reset propagates through the PID and produces a 0 output + wait_process : process (clk_i) + begin + if rising_edge(clk_i) then + if ENABLE_i = '1' then + wait_cntr <= 0; + else + if (pid_clkdiv_clr = '0') and (pid_ce_out = '1') and (wait_cntr/=WAIT_STATES) then + wait_cntr <= wait_cntr +1; + else + wait_cntr <= wait_cntr; + end if; + end if; + end if; + end process wait_process; + + -- synchronize external sampling clock to the PID internal 1 MHz clock (== ce_out pulse) + -- and supply it as CE_in to the PID IP + extfs_process : process (clk_i, sample_clk_i) + begin + if rising_edge(clk_i) then + if((sample_clk_i = '1') and (sample_clk_prev='0')) then + pid_ce_in <= '1'; + elsif((pid_ce_out = '1') and (pid_ce_out_prev='0')) then + pid_ce_in <= '0'; + else + pid_ce_in <= pid_ce_in; + end if; + end if; + end process extfs_process; + +end rtl; diff --git a/modules/pid/pid.block.ini b/modules/pid/pid.block.ini new file mode 100644 index 000000000..34b586d66 --- /dev/null +++ b/modules/pid/pid.block.ini @@ -0,0 +1,110 @@ +[.] +entity: pid +description: PID Controller +ip: pidmc_0 +extension: pid + +[RESERVED_GP] +type: extension_write uint +description: + +[RESERVED_GI] +type: extension_write uint +description: + +[RESERVED_G1D] +type: extension_write uint +description: + +[RESERVED_G2D] +type: extension_write uint +description: + +[ENABLE] +type: bit_mux +description: Enable when high, keep in reset when low + +[KP] +type: param scalar +description: Proportional Gain (Gmax=255) +extension: kP +extension_write: RESERVED_GP +scale: 119.20928955078e-9 + +[KI] +type: param scalar +description: Integral Gain (parallel form; Gmax=63) +extension: kI +extension_write: RESERVED_GI +scale: 62.5e-3 + +[KD] +type: param scalar +description: Derivative Gain (parallel form; Gmax=31) +extension: kD +extension_write: RESERVED_G1D RESERVED_G2D +scale: 7.8125e-3 + +[F_FILTER] +type: param scalar +description: Derivative filter cutoff frequency (Hz); must be < Fsampling/4 (NOT Fs/2); if in doubt, put it to Fs/5 +extension: f_filter +extension_write: RESERVED_G1D RESERVED_G2D +scale: 976.5625e-6 +units: Hz + +[F_SAMPLING] +type: param scalar +description: Sampling Frequecy (Hz, in range [1Hz,1MHz]); it must correspond to the frequency of the clock applied to "sample_clk" pin +extension: f_sample +extension_write: RESERVED_GI RESERVED_G1D RESERVED_G2D +scale: 1 +units: Hz + +[CMD] +type: pos_mux +description: Command (desired value) + +[MEAS] +type: pos_mux +description: Actual measured value + +[OUT] +type: pos_out +description: Control to plant +scale: 4.656612873e-10 + +[THRESH] +type: param scalar +description: Input threshold (full scale=1) no action is taken if the input error (=CMD-MEAS) is below this threshold: it is a deadband. This value can be 0. +scale: 9.313225746e-10 +units: FS1 + +[MAX_OUT] +type: param scalar +description: Output saturation limit (full scale=1) +scale: 9.313225746e-10 +units: FS1 + +[ANTI_INT_WNDP_RAT_G] +type: param scalar +description: Back calculation rational gain for anti integral windup. It is the ratio of kB (back calculation gain) and kI (integral gain). Usually, it should be =1; putting it to 0 disables the anti-integral windup. +scale: 9.313225746e-10 +units: FS1 + +[INV_CMD] +type: param bit +description: Invert commanded value sign + +[INV_MEAS] +type: param bit +description: Invert measured value sign + +[DERIV_ON_PROCVAR] +type: param bit +description: Derivative on process variable instead of on input error(=CMD-MEAS). Usually you want it to be 1. + +[SAMPLE_CLK] +type: bit_mux +description: sampling clock + diff --git a/modules/pid/pid_doc.rst b/modules/pid/pid_doc.rst new file mode 100644 index 000000000..376dd6117 --- /dev/null +++ b/modules/pid/pid_doc.rst @@ -0,0 +1,37 @@ +PID - PID controller +======================== + + + +The PID block is an implementation of the classical proportional/integral/derivative controller. +Its gains are intended as in +`parallel form `_ + + +The controller has protection features like: + + + - anti-integral windup + + - possibility to take the derivative of the process variable instead of the error + + +This is a discrete time implementation, with external sampling frequency in range [1Hz,1MHz]. The internal clock resampler frequency is reset by the ENABLE signal, so it's recommended to use the same signal for the ENABLE of the CLOCK block that generates the sampling frequency. + +Inputs can be inverted, for ease of implementation. Full scale is meant to be +/- 1 in the scaled PandaBlocks representation (or signed 32-bit int if raw representation is preferred). Internal calculations in 64 bit. + + + + + + + + +Fields +----------------------------------------------------- + + + +.. block_fields:: modules/pid/pid.block.ini + + diff --git a/modules/singen/hdl/singen.vhd b/modules/singen/hdl/singen.vhd new file mode 100755 index 000000000..667cd1bd0 --- /dev/null +++ b/modules/singen/hdl/singen.vhd @@ -0,0 +1,122 @@ +-------------------------------------------------------------------------------- +-- PandA Motion Project - 2024 +-- Diamond Light Source, Oxford, UK +-- SOLEIL Synchrotron, GIF-sur-YVETTE, France +-- MaxIV Laboratory, Lund, Sweden +-- +-------------------------------------------------------------------------------- +-- +-- Description : Sine wave generator +-- latest rev : feb 7 2024 +-- +-------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + + +entity singen is +port ( + -- Clock and Reset + clk_i : in std_logic; + ENABLE_i : in std_logic; + -- Block Input and Outputs + amplitude : in std_logic_vector(31 downto 0); -- sfix32_En30 + frequency : in std_logic_vector(31 downto 0); -- sfix32_En31 + out_o : out std_logic_vector(31 downto 0) -- sfix32_En31 + ); +end singen; + + +architecture rtl of singen is + + -- account for DDS pipelining + constant WAIT_STATES : natural := 13; + constant WAIT_CNTR_MAX : natural := 15; + + signal singen_clr : std_logic := '1'; + signal singen_res_n : std_logic := '0'; + signal singen_ce_out : std_logic; + signal wait_cntr : natural range 0 to WAIT_CNTR_MAX :=0; + signal singen_out : std_logic_vector(31 downto 0); + + component singenmc_0 + port + ( + rational_freq : in std_logic_vector(31 downto 0); + ampl : in std_logic_vector(31 downto 0); + reset_n : in std_logic_vector(0 downto 0); + clk : in std_logic; + clr : in std_logic; + sine_out : out std_logic_vector(31 downto 0); + ce_out : out std_logic_vector(0 downto 0) + ); + end component; + +begin + + the_singen: singenmc_0 + port map + ( + rational_freq => frequency, + ampl => amplitude, + reset_n(0) => singen_res_n, + clk => clk_i, + clr => singen_clr, + sine_out => singen_out, + ce_out(0) => singen_ce_out + ); + + reset_process : process (clk_i, ENABLE_i) + begin + if rising_edge(clk_i) then + if ENABLE_i = '0' then + singen_res_n <= '0'; + if (singen_clr = '0') and (wait_cntr=WAIT_STATES) then + singen_clr <= '1'; + else + singen_clr <= singen_clr; + end if; + else + singen_clr <= '0'; + if (singen_res_n = '0') and (singen_ce_out = '1') then + singen_res_n <= '1'; + else + singen_res_n <= singen_res_n; + end if; + end if; + end if; + end process reset_process; + + + wait_process : process (clk_i) + begin + if rising_edge(clk_i) then + if ENABLE_i = '1' then + wait_cntr <= 0; + else + if (singen_clr = '0') and (singen_ce_out = '1') then + wait_cntr <= wait_cntr +1; + else + wait_cntr <= wait_cntr; + end if; + end if; + end if; + end process wait_process; + + + -- first DDS sine lookup table value could be not exactly zero; + -- just output a solid 0 when disabled + out_process : process (clk_i) + begin + if rising_edge(clk_i) then + if ENABLE_i = '1' then + out_o <= singen_out; + else + out_o <= "00000000000000000000000000000000"; + end if; + end if; + end process out_process; + +end; diff --git a/modules/singen/singen.block.ini b/modules/singen/singen.block.ini new file mode 100755 index 000000000..ce171d8ff --- /dev/null +++ b/modules/singen/singen.block.ini @@ -0,0 +1,27 @@ +[.] +entity: singen +description: Sine wave generator +ip: singenmc_0 + +[ENABLE] +type: bit_mux +description: Enable when high, keep in reset when low + +[AMPLITUDE] +type: param scalar +description: Amplitude (full scale=1) +scale: 9.313225746e-10 +units: FS1 + +[FREQUENCY] +type: param scalar +description: Frequency (Hz; range [0.1 Hz, 500 kHz]) +scale: 4.656612873e-4 +units: Hz + +[OUT] +type: pos_out +description: Sine wave out (full scale=1) +scale: 4.656612873e-10 +units: FS1 + diff --git a/modules/singen/singen_doc.rst b/modules/singen/singen_doc.rst new file mode 100755 index 000000000..99b610cac --- /dev/null +++ b/modules/singen/singen_doc.rst @@ -0,0 +1,29 @@ +SINGEN - Sine Wave Generator +============================= + +The SINGEN block is a sine wave synthesizer. + +This is a discrete time implementation, with 1 MHz fixed sampling frequency: the same of the FMC_ACQ427 analog card block. + +Full scale is meant to be +/- 1 in the scaled PandaBlocks representation (or signed 32-bit int if raw representation is preferred). + +It can generate a sine wave with frequency in range [0.1 Hz, 500 kHz] + +Based on Xilinx DDS Compiler v6.0. + + +----------------------------------------------------- +Fields +----------------------------------------------------- + +.. block_fields:: modules/singen/singen.block.ini + + + + + + + + + +