Rust on Red Pitaya

Just about everything about Red Pitaya
Post Reply
Arnold
Posts: 54
Joined: Wed Mar 11, 2015 3:07 pm

Rust on Red Pitaya

Post by Arnold » Fri May 29, 2015 9:11 pm

Hi,

I recently started to learn Rust (http://www.rust-lang.org/). Rust recently stabilizes at version 1.0.

What is Rust?
A systems programming language that runs blazingly fast, prevents almost all crashes, and eliminates data races.

The following steps show to to setup the rust compiler and compile a simplified version of the generate application (can only generate a sine signal) to your RP :D

The used red pitaya's ecosystem is here (thanks to pavel), make sure that you set the right size for the uio in the patch file.
https://github.com/pavel-demin/red-pitaya-notes

I used /dev/uio in my app, bc I dont want to directly access /dev/mem. You can also use /dev/mem (not tested yet)...
If there is interest I can post my github...

Install the Rust Cross Compiler (Rust 1.0.0) for ARM (armv7l):

Code: Select all

  $ git clone https://github.com/rust-lang/rust
  $ cd rust
  $ git checkout stable
  $ ./configure --target=arm-unknown-linux-gnueabihf,x86_64-unknown-linux-gnu
  $ make -j$(nproc)
  $ sudo make install
Build Cargo (Reference: https://github.com/japaric/ruststrap/bl ... compile.md):

Code: Select all

  $ mkdir local_cargo
  $ curl -sSf https://static.rust-lang.org/rustup.sh | sh -s -- --prefix=local_cargo
Cross compile your Rust App with Cargo (To setup your cargo project read Reference):

Code: Select all

$ cargo build --target=arm-unknown-linux-gnueabihf
I ported https://github.com/RedPitaya/RedPitaya/ ... t/generate to Rust:
main.rs:

Code: Select all

// from crates.io
extern crate mmap;
extern crate libc;

use std::thread;
use std::env;
mod fpga_awg;

fn main() {
	if let Some(arg1) = env::args().nth(1) {
		let mut awg = fpga_awg::fpgaAwg::new();
		let mut params: fpga_awg::awgParam = fpga_awg::awgParam::default();
		let mut data: [i32; fpga_awg::n] = [0; fpga_awg::n];

		awg.syntesize_signal(1.0, 1000.0, &mut data, &mut params);
		awg.write_data_fpga(0, &mut data, &mut params);

		// blink led1...just for fun :)
		for i in 0..20 {
			awg.toggle(1);
			thread::sleep_ms(arg1.parse::<u32>().unwrap());
		}
	}
}
fpga_awg.rs:

Code: Select all

use std::ptr;
use std::fs;
use std::io::{Write, SeekFrom, Seek};
use std::os::unix::prelude::AsRawFd;
use mmap::{MemoryMap, MapOption};
use libc;
use std::f32;
use std::default::Default;

pub const n: usize = (16*1024);
const c_awg_smpl_freq: f32 = 125e6;
const AWG_BASE_ADDR: isize = 0x200000;
const AWG_CHA_OFFSET: usize = 0x210000;
const AWG_CHB_OFFSET: usize = 0x220000;

#[derive(Default)]
pub struct awgParam {
	offsgain: i32, //< AWG offset & gain.
	wrap: u32, //< AWG buffer wrap value.
	step: u32, //< AWG step interval.
}

struct awg_reg {
	state_machine_conf: u32,
	cha_scale_off: u32,
	cha_count_wrap: u32,
	cha_start_off: u32,
	cha_count_step: u32,
	reserved_regs: [u32; 4],
	chb_scale_off: u32,
	chb_count_wrap: u32,
	chb_start_off: u32,
	chb_count_step: u32,
}

pub struct fpgaAwg {
	mmap: MemoryMap,
	data: *mut u8,
	awgReg: *mut awg_reg,
	chaMem: *mut [u32; n],
	chbMem: *mut [u32; n],
}

impl fpgaAwg {
	pub fn new() -> fpgaAwg {	
		let size: usize = 0x400000;

		let mut f = fs::OpenOptions::new().read(true)
		                                  .write(true)
		                                  .open("/dev/uio0")
		                                  .unwrap();

		let mmap_opts = &[
		    // Then make the mapping *public* so it is written back to the file
		    MapOption::MapNonStandardFlags(libc::MAP_SHARED),
		    MapOption::MapReadable,
		    MapOption::MapWritable,
		    MapOption::MapFd(f.as_raw_fd()),
		];

		let mmap = MemoryMap::new(size, mmap_opts).unwrap();

		let data = mmap.data();

		if data.is_null() {
			panic!("Could not access data from memory mapped file")
		}
		else {
			println!("successful data access to memory mapped file");
		}

		let awgReg: *mut awg_reg;
		let chaMem: *mut [u32; n];
		let chbMem: *mut [u32; n];

		unsafe {
			awgReg = data.offset(AWG_BASE_ADDR) as *mut awg_reg;
			chaMem = data.offset(AWG_CHA_OFFSET as isize) as *mut [u32; n];
			chbMem = data.offset(AWG_CHB_OFFSET as isize) as *mut [u32; n];
		}

		fpgaAwg {mmap: mmap, data: data, awgReg: awgReg, chaMem: chaMem, chbMem: chbMem}
	}

	pub fn toggle(&mut self, led_pin: u32) {
		unsafe {
			*(self.data.offset(0x30) as *mut u32) ^= 1 << led_pin;
		}
	}

	pub fn syntesize_signal(&mut self, ampl: f32, freq: f32, data: &mut [i32; n], awg: &mut awgParam) {
		let mut amp: u32 = (ampl * 4000.0) as u32;
		if (amp > 8191) {
			amp = 8191;
		}

		const dcoffs: i32 = -155;

		unsafe {
			(*awg).offsgain = (dcoffs << 16) + 0x1fff;
			(*awg).step = (65536.0 * freq/c_awg_smpl_freq * n as f32).round() as u32;
			(*awg).wrap = (65536.0 * (n-1) as f32).round() as u32;

			for i in 0..n {
				(*data)[i] = (amp as f32 * (2.0 * f32::consts::PI * (i/n) as f32).cos()).round() as i32;
				
				if (*data)[i] < 0 {
					(*data)[i] += (1 << 14);
				}	
			}
		}
	}

	pub fn write_data_fpga(&mut self, ch: u32, data: *mut [i32; n], awg: *mut awgParam) {
		let i: u32;

		unsafe {
			if(ch == 0) {
				/* Channel A */
				(*self.awgReg).state_machine_conf = 0x000041;
				(*self.awgReg).cha_scale_off = (*awg).offsgain as u32;
				(*self.awgReg).cha_count_wrap = (*awg).wrap;
				(*self.awgReg).cha_count_step = (*awg).step;
				(*self.awgReg).cha_start_off = 0;

				for i in 0..n {
					(*self.chaMem)[i] = (*data)[i] as u32;
				}
			} else if (ch == 1) {
				/* Channel B */
				(*self.awgReg).state_machine_conf = 0x410000;
				(*self.awgReg).chb_scale_off = (*awg).offsgain as u32;
				(*self.awgReg).chb_count_wrap = (*awg).wrap;
				(*self.awgReg).chb_count_step = (*awg).step;
				(*self.awgReg).chb_start_off = 0;

				for i in 0..n {
					(*self.chbMem)[i] = (*data)[i] as u32;
				}
			} else {
				panic!("wrong channel selected");
			}
			/* Enable both channels */
			/* TODO: Should this only happen for the specified channel?
			* Otherwise, the not-to-be-affected channel is restarted as well
			* causing unwanted disturbances on that channel.
			*/
			(*self.awgReg).state_machine_conf = 0x110011;
		}
	}
}
acquire the generated signal, to be used for plotting:

Code: Select all

$ ./acquire 16000 64 | awk '{ print $1 "," $2}' > out.csv

Arnold
Posts: 54
Joined: Wed Mar 11, 2015 3:07 pm

Re: Rust on Red Pitaya

Post by Arnold » Tue Jun 02, 2015 9:02 pm

the following loop needs to be changed to for the release build (it works fin in debug), otherwise the code will crash with illegal instruction, reason: wrong code optimization: stmiacs r3!, {r1, r12} ... it stores two 32-bit values at a time...

From:

Code: Select all

for i in 0..n {
               (*self.chaMem)[i] = (*data)[i] as u32;
}
To:

Code: Select all

for i in 0..n {
              volatile_store::<u32>(&mut (*self.chaMem)[i], (*data)[i] as u32);
}
you will need to use the nightly (development build) build to make use of the volatile_store operation...
same applies to the second loop using chbMem...

another quick fix (which works with the stable version of rust 1.0) is to put the following in front of the function, when using the release build:

Code: Select all

#[inline(never)]

Nils Roos
Posts: 1441
Joined: Sat Jun 07, 2014 12:49 pm
Location: Königswinter

Re: Rust on Red Pitaya

Post by Nils Roos » Thu Jun 04, 2015 1:36 pm

Rust on the Red Pitaya - an interesting proposition. But seriously, nice work.

Have you compared the performance of the Rust-based generate - particularly the copying to the buffer - with the original C-based application ? I would be very interested how Rust performs.

Arnold
Posts: 54
Joined: Wed Mar 11, 2015 3:07 pm

Re: Rust on Red Pitaya

Post by Arnold » Thu Jun 04, 2015 7:48 pm

i could do it on the acquire. how about trigger on a signal + copy 1000 samples into a buffer and measure the time?
...but i need both C and Rust to optimize the code with -O3, to make a fair comparison.

Post Reply
jadalnie klasyczne ekskluzywne meble wypoczynkowe do salonu ekskluzywne meble tapicerowane ekskluzywne meble do sypialni ekskluzywne meble włoskie

Who is online

Users browsing this forum: No registered users and 28 guests