Skip to content

Commit

Permalink
Dac router (#318)
Browse files Browse the repository at this point in the history
* clean dac_router

* remove set_dac_float from oscillo.cpp

* add dac_router module and remove base project

* fix dac_router.tcl
  • Loading branch information
jeanminet committed Sep 16, 2016
1 parent ce97fd9 commit 4f60080
Show file tree
Hide file tree
Showing 12 changed files with 199 additions and 221 deletions.
173 changes: 56 additions & 117 deletions drivers/lib/dac_router.hpp
Expand Up @@ -7,28 +7,11 @@

#include "memory_manager.hpp"

constexpr uint32_t dac_sel_width(uint32_t n_dac_bram) {
constexpr uint32_t get_width(uint32_t n_dac_bram) {
return ceil(log(float(n_dac_bram)) / log(2.));
}

static_assert(dac_sel_width(3) == 2, "dac_sel_width test failed");

constexpr uint32_t bram_sel_width(uint32_t n_dac) {
return ceil(log(float(n_dac)) / log(2.));
}

static_assert(bram_sel_width(2) == 1, "bram_sel_width test failed");

// http://stackoverflow.com/questions/12276675/modulus-with-negative-numbers-in-c
constexpr long mod(long a, long b) {
return (a % b + b) % b;
}

constexpr uint32_t half_dynamic_range(uint32_t n_bits) {
return 1 << (n_bits - 1);
}

static_assert(half_dynamic_range(14) == 8192, "half_dynamic_range test failed");
static_assert(get_width(8) == 3, "get_width test failed");
static_assert(get_width(9) == 4, "get_width test failed");

template<uint32_t n_dac, uint32_t n_dac_bram>
class DacRouter
Expand All @@ -42,127 +25,83 @@ class DacRouter
void set_config_reg(uint32_t dac_select_off_, uint32_t addr_select_off_) {
dac_select_off = dac_select_off_;
addr_select_off = addr_select_off_;

init_dac_brams();
}

uint32_t* get_data(uint32_t channel) {
return dac_map[bram_index[channel]].get_ptr();
}

template<size_t N>
std::array<uint32_t, N>& get_data(uint32_t channel) {
return dac_map.read_array<uint32_t, N>(bram_index[channel]);
}

void set_data(uint32_t channel, const uint32_t *buffer, uint32_t len);

/// /!\ Array is of size 1/2 of the number of samples in the waveform
template<size_t N>
void set_data(uint32_t channel, const std::array<uint32_t, N> arr) {
set_data(channel, arr.data(), N);
uint32_t old_idx = bram_index[channel];
uint32_t new_idx = get_first_empty_bram_index();
dac_map.set_ptr(arr.data(), N, new_idx);
switch_interconnect(channel, old_idx, new_idx);
}

/// The waveform is an array of floats between -1 and 1.
/// Array is of size the number of samples in the waveform.
template<uint32_t n_bits, size_t N>
void set_data(uint32_t channel, const std::array<float, N> arr);

private:
Memory<mem::config>& cfg;
uint32_t dac_select_off; // TODO Known at compile time
uint32_t addr_select_off; // TODO Known at compile time
uint32_t dac_select_off;
uint32_t addr_select_off;
Memory<mem::dac>& dac_map;

static_assert(n_dac_bram == mem::get_n_blocks(mem::dac), "Invalid n_dac_bram");

std::array<uint32_t, n_dac> bram_index;
std::array<bool, n_dac_bram> connected_bram;

void init_dac_brams();
void update_dac_routing();
int get_first_empty_bram_index();
void switch_interconnect(uint32_t channel, uint32_t old_idx, uint32_t new_idx);

template<uint32_t n_bits>
uint32_t convert_data(float val) {
constexpr uint32_t half_dyn_range = half_dynamic_range(n_bits);
return mod(static_cast<uint32_t>(floor(half_dyn_range * val) + half_dyn_range), 2 * half_dyn_range) + half_dyn_range;
void init_dac_brams() {
// Use BRAM0 for DAC0, BRAM1 for DAC1 ...
for (uint32_t i=0; i < n_dac; i++) {
bram_index[i] = i;
connected_bram[i] = true;
}
for (uint32_t i=n_dac; i < n_dac_bram; i++) {
connected_bram[i] = false;
}
update_dac_routing();
}
};

template<uint32_t n_dac, uint32_t n_dac_bram>
inline void DacRouter<n_dac, n_dac_bram>::init_dac_brams()
{
// Use BRAM0 for DAC0, BRAM1 for DAC1 ...
for (uint32_t i=0; i < n_dac; i++) {
bram_index[i] = i;
connected_bram[i] = true;
void update_dac_routing() {
// dac_select defines the connection between BRAMs and DACs
uint32_t dac_select = 0;
for (uint32_t i=0; i < n_dac_bram; i++) {
dac_select += bram_index[i] << (get_width(n_dac_bram) * i);
}
cfg.write_reg(dac_select_off, dac_select);

// addr_select defines the connection between address generators and BRAMs
uint32_t addr_select = 0;
for (uint32_t j=0; j < n_dac_bram; j++) {
addr_select += j << (get_width(n_dac) * bram_index[j]);
}
cfg.write_reg(addr_select_off, addr_select);
}

for (uint32_t i=n_dac; i < n_dac_bram; i++)
connected_bram[i] = false;

update_dac_routing();
}

template<uint32_t n_dac, uint32_t n_dac_bram>
inline void DacRouter<n_dac, n_dac_bram>::update_dac_routing()
{
// dac_select defines the connection between BRAMs and DACs
uint32_t dac_select = 0;
for (uint32_t i=0; i < n_dac_bram; i++)
dac_select += bram_index[i] << (dac_sel_width(n_dac_bram) * i);
cfg.write_reg(dac_select_off, dac_select);

// addr_select defines the connection between address generators and BRAMs
uint32_t addr_select = 0;
for (uint32_t j=0; j < n_dac_bram; j++)
addr_select += j << (bram_sel_width(n_dac) * bram_index[j]);
cfg.write_reg(addr_select_off, addr_select);
}

template<uint32_t n_dac, uint32_t n_dac_bram>
inline int DacRouter<n_dac, n_dac_bram>::get_first_empty_bram_index()
{
for (uint32_t i=0; i < n_dac_bram; i++)
if ((bram_index[0] != i) && (bram_index[1] != i))
return i;
return -1;
}

template<uint32_t n_dac, uint32_t n_dac_bram>
inline void DacRouter<n_dac, n_dac_bram>::switch_interconnect(
uint32_t channel, uint32_t old_idx, uint32_t new_idx)
{
bram_index[channel] = new_idx;
connected_bram[new_idx] = true;
update_dac_routing();
connected_bram[old_idx] = false;
}

template<uint32_t n_dac, uint32_t n_dac_bram>
inline void DacRouter<n_dac, n_dac_bram>::set_data(
uint32_t channel, const uint32_t *buffer, uint32_t len)
{
uint32_t old_idx = bram_index[channel];
uint32_t new_idx = get_first_empty_bram_index();
dac_map.set_ptr(buffer, len, new_idx);
switch_interconnect(channel, old_idx, new_idx);
}

template<uint32_t n_dac, uint32_t n_dac_bram>
template<uint32_t n_bits, size_t N>
inline void DacRouter<n_dac, n_dac_bram>::set_data(
uint32_t channel, const std::array<float, N> arr)
{
static_assert(N % 2 == 0, "Waveform must have an even number of samples");
std::array<uint32_t, N/2> data;
uint32_t get_first_empty_bram_index() {
for (uint32_t i=0; i < n_dac_bram; i++) {
// Check if the BRAM is connected to no
bool empty = true;
for (uint32_t j=0; j < n_dac; j++) {
if (bram_index[j] == i) {
empty = false;
break;
}
}
if (empty) {
return i;
}
}
return -1;
}

for (uint32_t i=0, j=0; i<N; i+=2, j++)
data[j] = convert_data<n_bits>(arr[i]) + (convert_data<n_bits>(arr[i + 1]) << 16);
void switch_interconnect(uint32_t channel, uint32_t old_idx, uint32_t new_idx) {
bram_index[channel] = new_idx;
connected_bram[new_idx] = true;
update_dac_routing();
connected_bram[old_idx] = false;
}

set_data(channel, data);
}
};

#endif // __DRIVERS_LIB_DAC_ROUTER_HPP__
68 changes: 68 additions & 0 deletions fpga/lib/dac_router.tcl
@@ -0,0 +1,68 @@
source fpga/lib/bram.tcl

# Single BRAM recorder (32 bit width)

proc add_dac_router {module_name memory_name {intercon_idx 0}} {

set bd [current_bd_instance .]
current_bd_instance [create_bd_cell -type hier $module_name]

create_bd_pin -dir I -from 31 -to 0 addr_select
create_bd_pin -dir I -from 31 -to 0 dac_select
create_bd_pin -dir I -type clk clk
create_bd_pin -dir I rst
create_bd_pin -dir I clken

for {set i 0} {$i < [get_parameter n_dac]} {incr i} {
create_bd_pin -dir I -from [expr [get_memory_addr_width $memory_name] + 2] -to 0 addr$i
}

for {set i 0} {$i < [get_parameter n_dac]} {incr i} {
create_bd_pin -dir O -from [expr [get_parameter dac_width]-1] -to 0 dout$i
}

source fpga/lib/interconnect.tcl
set addr_intercon_name addr_intercon
interconnect::create $addr_intercon_name [expr [get_memory_addr_width $memory_name] + 3] [get_parameter n_dac] [get_parameter n_dac_bram]

connect_cell $addr_intercon_name {
clk clk
sel addr_select
clken clken
}

for {set i 0} {$i < [get_parameter n_dac]} {incr i} {
connect_pins $addr_intercon_name/in$i addr$i
}

# Add DAC controller

source fpga/lib/dac_controller.tcl

set interconnect_name dac_interconnect
interconnect::create $interconnect_name [get_parameter dac_width] [get_parameter n_dac_bram] [get_parameter n_dac]

connect_cell $interconnect_name {
clk clk
sel dac_select
clken clken
}

for {set i 0} {$i < [get_parameter n_dac]} {incr i} {
connect_pins $interconnect_name/out$i dout$i
}

for {set i 0} {$i < [get_parameter n_dac_bram]} {incr i} {
set dac_controller_name dac${i}_ctrl
add_single_dac_controller $dac_controller_name dac$i [get_parameter dac_width] 1
connect_cell $dac_controller_name {
clk clk
addr $addr_intercon_name/out$i
rst rst
dac $interconnect_name/in$i
}
}

current_bd_instance $bd

}
70 changes: 0 additions & 70 deletions instruments/base/block_design.tcl

This file was deleted.

18 changes: 0 additions & 18 deletions instruments/base/config.yml

This file was deleted.

11 changes: 9 additions & 2 deletions instruments/oscillo/block_design.tcl
@@ -1,4 +1,11 @@
source instruments/base/block_design.tcl

source instruments/oscillo/init.tcl

# Source Oscillo.tcl
source instruments/oscillo/oscillo.tcl
source instruments/base/timer.tcl

# Timer
source instruments/oscillo/timer.tcl


source fpga/lib/at93c46d.tcl

0 comments on commit 4f60080

Please sign in to comment.