Previous blogs: Introduction I have a small Brevia 2 development board from Lattice Semiconductor featuring one of their XP2-family FPGAs. I'm using it to explore, in a very simple, basic kind of way, digital signal generation and processing. These blogs aren't tutorials and there's no structured path to any of it: it's just me experimenting and trying things out.  In this blog, I've diverted away from sinewaves and I'm experimenting with random sequences. Not the truly random sort, such as you might get from a natural process like thermal noise or radioactive decay, but a computed sequence that has similar statistical properties to a random one. The two books that got me started on this were 'Seminumerical Algorithms' by Donald Knuth and 'Numerical Recipes In C' by Press, Teukolsky, Vettering, and Flannery.  Linear Congruential Generator The generator I'm going to try for here is a simple one given in Numerical Recipes in C, though the multiplier and constant are due to Knuth. The formula is new_random = last_random * 1664525 + 1013904223 (mod 2^32) It's quick, because it only involves a multiply and an addition, though I don't really need speed as I'm only generating a sample every 10us. The unsigned multiplier and adder I'll generate with IPExpress. Then it's just a matter of wiring in the constants and picking off the high part of the result to send to the DAC, with the code to send to the DAC just copy-and-pasted from a previous blog. I didn't register the multiplier: I figured that the arithmetic would complete quite easily in the 10us between samples. If you wanted this to run at a fast clock rate, you'd need to look carefully at the timing through the two components. Here's the code, usual caveats apply. ```------------------------------------------------------------------ --              ***** waves_random_word *****                  -- -- Random number generator                                      -- ------------------------------------------------------------------ -- JC 10th November 2019                                        -- ------------------------------------------------------------------ -- Rev    Date         Comments                                 -- -- 01     10-Nov-2019                                           -- ------------------------------------------------------------------ library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_arith.all; use ieee.std_logic_unsigned.all; entity waves_random_word_con is port(   clk_in:   in std_logic;        --- system clock in (50 MHz oscillator)   --- DAC connections   mcp4821_ncs: out std_logic;        --- DAC cs   mcp4821_sck: out std_logic;        --- DAC sck   mcp4821_sdi: out std_logic;        --- DAC sdi   mcp4821_nldac: out std_logic;        --- DAC load   --- misc control signals on evaluation board that it might be good to hold at fixed levels   spi_cs:   out std_logic;        ---   hold_n:   out std_logic;        ---   sram_cen:  out std_logic;        ---   sram_oen:  out std_logic;        ---   sram_wen:  out std_logic;        ---   uart_tx:  out std_logic);        --- end waves_random_word_con; architecture arch_waves_random_word of waves_random_word_con is signal spi_send, spi_ncs, spi_ncs_del: std_logic; ---signal waveform_count: std_logic_vector (11 downto 0); signal spi_output_sr_bit_count: std_logic_vector (5 downto 0); signal spi_output_sr: std_logic_vector (15 downto 0); signal interval_count: std_logic_vector (8 downto 0); signal random_value: std_logic_vector (31 downto 0); signal mult_result: std_logic_vector (63 downto 0); signal multiplier_value: std_logic_vector (31 downto 0); signal constant_value: std_logic_vector (31 downto 0); signal add_result: std_logic_vector (31 downto 0); --- declare the multiplier and the adder modules as components component mult_module is     port (         DataA: in  std_logic_vector(31 downto 0);         DataB: in  std_logic_vector(31 downto 0);         Result: out  std_logic_vector(63 downto 0)); end component; component add_module is     port (         DataA: in  std_logic_vector(31 downto 0);         DataB: in  std_logic_vector(31 downto 0);         Result: out  std_logic_vector(31 downto 0)); end component; begin waves_random_word_stuff: process (clk_in)   begin    if (clk_in'event and clk_in='1') then       --- interval_count counts at the clock rate (50MHz)     --- count divides by 500, so spi_send occurs every 10us (100ksps)     if (interval_count(8 downto 0) = b"000000000") then     --- if zero      interval_count(8 downto 0) <= b"111110011";      --- preset to 499      spi_send <= '1';     else      interval_count(8 downto 0) <= interval_count(8 downto 0) - 1; --- count down      spi_send <= '0';     end if;     --- spi ncs goes low when triggered by spi_send, goes hi again when bitcount reaches 31         if (spi_send = '1') then      spi_ncs <= '0';     elsif (spi_output_sr_bit_count(5 downto 0) = b"111111") then      spi_ncs <= '1';     end if;         spi_ncs_del <= spi_ncs;     --- spi output bit count only counts when enable spi cs is low         if (spi_ncs = '0') then      spi_output_sr_bit_count(5 downto 0) <= spi_output_sr_bit_count(5 downto 0) + 1;     else      spi_output_sr_bit_count(5 downto 0) <= b"000000";     end if;     --- sequence generator     --- this is storage part     --- see the instantiated components below for the rest     if (spi_send = '1') then      random_value(31 downto 0) <= add_result(31 downto 0);     end if;     --- spi output shift register         if (spi_send = '1') then             --- load register...      spi_output_sr(11 downto 0) <= random_value(31 downto 20);    ---  random data      spi_output_sr(15 downto 12) <= b"0011";         ---   dac control bits     elsif (spi_ncs = '0' and spi_output_sr_bit_count(1 downto 0) = b"11") then --- shift register...      spi_output_sr(15 downto 1) <= spi_output_sr(14 downto 0);    ---  the register contents      spi_output_sr(0) <= '0';     end if;    end if;       --- generator control parameters       multiplier_value <= b"00000000000110010110011000001101";    constant_value <=  b"00111100011011101111001101011111";    --- connecting various signals to output pins       mcp4821_ncs <= spi_ncs;    mcp4821_sck <= spi_output_sr_bit_count(1);    mcp4821_sdi <= spi_output_sr(15);    mcp4821_nldac <= '0';      --- hold these device control pins at a fixed level to stop them flapping around    spi_cs <= '1';    hold_n <= '1';    sram_cen <= '1';    sram_oen <= '1';    sram_wen <= '1';    uart_tx <= '1';   end process waves_random_word_stuff;   --- instantiate the multiplier and the subtractor for the oscillator   --- and make the signal connections     mult_module1: mult_module    port map(     DataA => multiplier_value(31 downto 0),     DataB => random_value(31 downto 0),     Result => mult_result(63 downto 0));       add_module1: add_module    port map(     DataA => constant_value(31 downto 0),     DataB => mult_result(31 downto 0),     Result => add_result(31 downto 0)); end arch_waves_random_word; ``` Here's how the output looks. The blue trace is the DAC output, the yellow the filter output. The filtered output is delayed somewhat [which, of course, is inevitable with an analogue filter].  Here's how multiple traces look. Away from the trigger point, that looks quite satisfyingly noisy.  Here it is on a longer trace  Finally, here's the best the 'scope can manage in the way of an FFT. This is accumulated over several seconds. The cut-off of the reconstruction filter is somewhere close to the centre of the screen, so the noise is roughly tracing out its response for me [roughly because the sinx/x response of the zero-order hold of the DAC will also be mixed in with it].   Below the cut-off, the spectrum is reasonably level, so there the numbers I'm generating approximate to 'white' noise. That was very quick and easy to do. It doesn't take many slices but it's quite expensive in one respect because it uses up one DSP block to build the 32 x 32 multiplier. Trade Offs Here is the floor plan using the hard multiplier in the DSP block (the 36x36 multiplier is the turquoise block).   MULT36             1/3            33% used   SLICE             49/2376          2% used   This, in contrast, is the floorplan if I ask IPExpress to build the multiplier from slices.   SLICE            336/2376         14% used  Conclusion That was an interesting little diversion on my journey. Now I've got to decide whether to go back to CORDIC routines or continue investigating noise.