[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
#include
#include
#include
#include
#include
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)