[LNIV Red Pitaya](index.html) # 3. [Red Pitaya](https://www.redpitaya.com/) signal processing: Data sampling An example of simple data sampling osciloscope component in VHDL is described. Design is based on the Vivado project from the [signal processing tutorial](redpitaya-proc.htm), where a custom component **proc** is connected to ADC or signal generator input channels: ![](./img/RedPitaya_proc.png) ## Data sampling logic The processing component from the first tutorial is modified to work as a very simple scope: [proc2.vhd](proc2.vhd) We upgraded the component with declaration of 256 x 8-bit memory block, memory write and read counters, memory data and scope state signals: ``` type ram_type is array (0 to 255) of signed(7 downto 0); signal ram : ram_type := (others => "00000000"); signal we: std_logic := '0'; signal rcnt, wcnt: unsigned(7 downto 0) := x"00"; signal dat_ram: signed(7 downto 0) := (others=>'0'); signal st: std_logic_vector(1 downto 0) := "00"; -- scope state: 0-idle, 1-enable, 2-sample ``` Memory writing and reading is modeled in a synchronous process. The memory input data are upper 8-bits of the scaled (and saturated) signal value. Signal **we** controls data writing. ``` pram: process(clk_i) begin if rising_edge(clk_i) then if we='1' then ram(to_integer(wcnt)) <= signed(dat_a(13 downto 6)); end if; dat_ram <= ram(to_integer(rcnt)); end if; end process; ``` Memory reading is performed when the processor reads data at the address 100C. When the read request falls to 0, we increment read counter **rcnt** to prepare the next data sample. ``` with sys_addr(19 downto 0) select sys_rdata <= X"FE02021" & "00" & st when x"01000", -- ID & status X"000000" & a when x"01004", -- 8-bit amplitude a X"000000" & std_logic_vector(dat_ram) when x"0100C", -- 8-bit ram data ... -- system bus read, increment read pointer on falling edge of sys_ren if sys_addr(19 downto 0)=X"0100C" and sys_ren='1' then sys_ren1 <= '1'; else sys_ren1 <= '0'; end if; if sys_ren1='1' and sys_ren='0' then rcnt <= rcnt + 1; end if; ``` The scope is controlled by a state machine. When the processor writes to address 1000, the 2-bit state changes to start (01) and after trigger to write state (01). In the write state we activate memory write signal and increment write counter. When the write counter reaches max value (FF), the state changes to stop (10) to indicate that the memory is full. ![](./img/fsm.png) A simple zero crossing trigger is implemented with two comparators and flip-flops. The first trigger flip-flop is set when the signal value is positive and reset when the value falls under 0. The second flip-flop us used to detect rising edge of the trigger signal. ![](./img/trig.png) ## Simulation Signal processing component should be verified with simulation using provided test bench [tb_proc2.vhd](tb_proc2.vhd). The testbench generates control signals (clk_i, rstn_i), simulates system bus transactions to set registers and produces sine waveform on the signal input. The test bench starts data sampling by writing 1 to the address 1000, waits several hundred cycles and performs sequential reading of the sample memory: ``` for i in 0 to 250 loop -- read memory addr_i <= x"0000100C"; ren_i <= '1'; wait for T; ren_i <= '0'; wait for T; end loop; ``` In the simulation waveform we can observe the read values on the lower 8 bits of sys_rdata: ![](./img/scopesim.png) ## Test on STEMlab We prepared a test C code to read sampled values on the STEMlab. ```C #include <stdio.h> #include <stdint.h> #include <unistd.h> #include <sys/mman.h> #include <fcntl.h> #include <stdlib.h> void Out32(void *adr, int offset, int value) { *((uint32_t *)(adr+offset)) = value; } int In8(void *adr, int offset) { int d=*((uint32_t *)(adr+offset)); return d>127 ? d-256 : d; } int main(int argc, char **argv) { int fd; int BASE = 0x40301000; // Proc base address; void *adr; char *name = "/dev/mem"; int i,d; if((fd = open(name, O_RDWR)) < 0) { // open memory device perror("open"); return 1; } // map the memory, start from BASE address, size: _SC_PAGESIZE = 4k adr = mmap(NULL, sysconf(_SC_PAGESIZE), PROT_READ|PROT_WRITE, MAP_SHARED, fd, BASE); Out32(adr, 0, 1); // start scope sleep(1); printf("x, y\n"); for (i=0; i<255; i++) { d = In8(adr, 12); printf("%d, %d\n",i,d); } munmap(adr, sysconf(_SC_PAGESIZE)); return 0; } ``` The sampled data is output on the terminal and can be redirected to a comma separated value file. By transfering the file to the host we can obtain sample plot: ``` root@rp:~# ./sample > vz.csv ``` ![](./img/csvplot.png)