Saturday, July 13, 2024

RE: Writing Drivers for Custom Hardware

I attempted to write an OOT block that would read and write from/to the hackrf. I never got it to work. Based on that experience, here's some advice:

First build a simple block like the example in the tutorial. Pay careful attention to the version of Gnuradio you are using and the version the tutorial uses.

Look at the code for the rtlsdr source block and figure out how each of Marcus' suggestions is implemented for that hardware.

Learn about logging and debugging techniques that can be used in an OOT environment. Especially important to understand the concepts of threads.

Make backups at each milestone so you can get back to a known state when you mess things up.

You're already on the support mailing list; now learn how to search past posts to see if anyone else has faced the same issue as you. Eg try searching for posts from me.

Hope that helps,
Gavin








> Message: 6
> Date: Sat, 13 Jul 2024 16:32:46 +0200
> From: Marcus Müller <mmueller@gnuradio.org>
> To: discuss-gnuradio@gnu.org
> Subject: Re: Writing Drivers for Custom Hardware
> Message-ID: <3aeb60bd-5e7e-4cff-a8da-094f62b2b43a@gnuradio.org>
> Content-Type: text/plain; charset=UTF-8; format=flowed
>
> So, to your core question, writing a GNU Radio block for your device is relatively easy,
> probably; data rates aren't *that* high, so an extra memory copy here and there is
> something I'd live with for a prototype.
> Methodology would be this, roughly:
> 1. make an out-of-tree module. We cover this on https://tutorials.gnuradio.org ,
> specifically in [1]. In short, `gr_modtool newmod yourmodname`.
> 2. Make a source block (`gr_modtool add -t source -l c++ hose_source`)
> 3. in the generated lib/something_impl.cc, add a `bool hose_source::start() {}`, and also
> add tht `bool start() override;` method prototype to the _impl.h
> 4. in the _impl.h add private fields: a set of buffers, one for each channel, where you'll
> put the data "deinterleavedly" from the NIC. Make each buffer some (say, 2²⁰) GNSS samples
> long. Also add two unsigned integer counters: one read and one write index. And because
> we're lazy and don't care *that* much about performance yet, two mutexes (one for securing
> access to the read index, and one for the write index).
> 5. in the constructor, you allocate these buffers, set the read index to the length of the
> buffers (-1) and the write index to 0
> 6. in the start() method, you spawn a thread that, in a loop
> 1. checks how much space there is between read and write index (get read mutex, fetch
> read index value, release mutex, calculate difference)
> 2. uses pcap to fetch packets, (but only as much as the space calculated above allows
> for!), deinterleaves data onto the buffers, finally
> 4. updates write index (get write mutex, update write index, release write mutex)
> 7. the block's work() method is called by GNU Radio regularly and
> 1. checks how much data is between write and read indices (get write mutex, read write
> index, release mutex, calculate difference)
> 2. checks whether that's more or less than the space for output items available in this
> current call to work(), takes the minimum of both
> 3. gets that amount of items from each buffer and writes them to the output buffer, as
> passed as argument to the work() method (you could do type conversions here!)
> 4. updates the read index accordingly (get read mutex, update index, release mutex)
> 5. returns the number of written items
>
> note that the index updating and distance calculation need to take the "wraparound" at the
> end of the buffer into account.
>
> Also note: very similarly, you could write a **SoapySDR** driver instead of a GNU Radio
> block. You could then use the Generic SoapySDR Source block to get data from that driver,
> and other, non-GNU Radio programs, could be using the driver just as well, without knowing
> about the hardware. I don't think the basic principle would be much different: you need an
> IO thread that keeps the NIC busy, and because readers might be slow, an internal buffer,
> which you ideally use constructively (instead of just incurring a memory bandwidth
> overhead), to deinterleave channels on ingress, and to convert data types on egress, if
> you

No comments:

Post a Comment