Skip to content

Commit 245205f

Browse files
committed
[seq/zynq] Add helper for converting between floating point and machine unit
1 parent 13fcd67 commit 245205f

6 files changed

Lines changed: 112 additions & 62 deletions

File tree

lib/nacs-seq/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ set(nacs_seq_zynq_HDRS
1111
zynq/cmdlist.h
1212
zynq/pulse_time.h
1313
zynq/seq_shared.h
14-
zynq/legacy_seq.h)
14+
zynq/legacy_seq.h
15+
zynq/utils.h)
1516
set(nacs_seq_nidaq_HDRS
1617
nidaq/backend.h
1718
nidaq/data_gen.h)

lib/nacs-seq/zynq/bc_gen.cpp

Lines changed: 9 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "bc_gen.h"
2020

2121
#include "bytecode.h"
22+
#include "utils.h"
2223
#include "../../nacs-utils/processor.h"
2324

2425
#include <string.h>
@@ -461,37 +462,14 @@ NACS_EXPORT() uint32_t BCGen::convert_value(ChnType type, double value)
461462
switch (type) {
462463
case ChnType::TTL:
463464
return value != 0;
464-
case ChnType::Freq: {
465-
constexpr double factor = 1.0 * (1 << 16) * (1 << 16) / 3.5e9;
466-
if (value <= 0)
467-
return 0;
468-
// The instruction only have 31 bits so it actually won't encode exactly 1.75 GHz.
469-
if (value > 1.7499999987776392e9) // last float that would round to 2^31 - 2
470-
return 0x7fffffff;
471-
return round<int32_t>(value * factor);
472-
}
473-
case ChnType::Amp: {
474-
constexpr double factor = 4095;
475-
if (value <= 0)
476-
return 0;
477-
if (value >= 1)
478-
return 4095;
479-
return round<int32_t>(value * factor);
480-
}
481-
case ChnType::Phase: {
482-
// Value is phase in [0, 1]
483-
return round<int32_t>(value * (1 << 16)) & ((1 << 16) - 1);
484-
}
485-
case ChnType::DAC: {
486-
constexpr double factor = 65535 / 20.0;
487-
constexpr double offset = 10.0;
488-
if (value <= -10)
489-
return 0;
490-
if (value >= 10)
491-
return 0xffff;
492-
value += offset;
493-
return round<int32_t>(value * factor);
494-
}
465+
case ChnType::Freq:
466+
return dds_freq_to_mu(value);
467+
case ChnType::Amp:
468+
return dds_amp_to_mu(value);
469+
case ChnType::Phase:
470+
return dds_phase_to_mu(value);
471+
case ChnType::DAC:
472+
return dac_to_mu(value);
495473
default:
496474
return 0;
497475
}

lib/nacs-seq/zynq/exehelper_p.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <ostream>
2323

2424
#include "pulse_time.h"
25+
#include "utils.h"
2526

2627
// This file contains a few classes/functions that are useful for both bytecode
2728
// and command list.
@@ -61,8 +62,7 @@ struct Printer {
6162
{
6263
stm << "freq(" << int(chn) << ")=";
6364
if (dec) {
64-
constexpr double factor = 3.5e9 / (1 << 16) / (1 << 16);
65-
stm << double(freq) * factor << std::endl;
65+
stm << dds_freq_from_mu(freq) << std::endl;
6666
}
6767
else {
6868
stm << "0x" << std::hex << freq << std::dec << std::endl;
@@ -72,7 +72,7 @@ struct Printer {
7272
{
7373
stm << "amp(" << int(chn) << ")=";
7474
if (dec) {
75-
stm << amp / 4095.0 << std::endl;
75+
stm << dds_amp_from_mu(amp) << std::endl;
7676
}
7777
else {
7878
stm << "0x" << std::hex << amp << std::dec << std::endl;
@@ -82,7 +82,7 @@ struct Printer {
8282
{
8383
stm << "phase(" << int(chn) << ")=";
8484
if (dec) {
85-
stm << phase / double(1 << 16) << std::endl;
85+
stm << dds_phase_from_mu(phase) << std::endl;
8686
}
8787
else {
8888
stm << "0x" << std::hex << phase << std::dec << std::endl;

lib/nacs-seq/zynq/legacy_seq.cpp

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#include "legacy_seq.h"
2020
#include "bytecode.h"
21+
#include "utils.h"
2122

2223
#include "../../nacs-utils/number.h"
2324
#include "../../nacs-utils/streams.h"
@@ -501,12 +502,9 @@ class Writer {
501502
return PulseTime::Clock;
502503
}
503504

504-
static constexpr double freq_factor = 1.0 * (1 << 16) * (1 << 16) / 3.5e9;
505505
int addDDSFreq(uint64_t t, uint8_t chn, double freqf)
506506
{
507-
uint32_t freq = uint32_t(0.5 + freqf * freq_factor);
508-
if (freq > 0x7fffffff)
509-
freq = 0x7fffffff;
507+
uint32_t freq = dds_freq_to_mu(freqf);
510508
if (dds[chn].freq_set && freq == dds[chn].freq)
511509
return 0;
512510
addWait(t - cur_t);
@@ -536,9 +534,7 @@ class Writer {
536534

537535
int addDDSAmp(uint64_t t, uint8_t chn, double ampf)
538536
{
539-
uint16_t amp = uint16_t(ampf * 4095.0 + 0.5);
540-
if (amp > 4095)
541-
amp = 4095;
537+
uint16_t amp = dds_amp_to_mu(ampf);
542538
if (dds[chn].amp_set && amp == dds[chn].amp)
543539
return 0;
544540
addWait(t - cur_t);
@@ -560,19 +556,7 @@ class Writer {
560556

561557
int addDAC(uint64_t t, uint8_t chn, double Vf)
562558
{
563-
uint16_t V;
564-
if (Vf >= 10) {
565-
V = 0;
566-
}
567-
else if (Vf <= -10) {
568-
V = 0xffff;
569-
}
570-
else {
571-
// this is for the DAC8814 chip in SPI0
572-
double scale = 65535 / 20.0;
573-
double offset = 10.0;
574-
V = uint16_t(((offset - Vf) * scale) + 0.5);
575-
}
559+
uint16_t V = dac_to_mu(Vf);
576560
if (dac[chn].set && V == dac[chn].V)
577561
return 0;
578562
addWait(t - cur_t);

lib/nacs-seq/zynq/parser.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#include "parser_p.h"
2020
#include "pulse_time.h"
21+
#include "utils.h"
2122

2223
#include "../../nacs-utils/errors.h"
2324

@@ -347,8 +348,7 @@ std::pair<uint8_t,uint32_t> ParserBase::read_freqcmd()
347348
}
348349
if (freq_hz > 1.75e9)
349350
syntax_error("Frequency too high (max 1.75GHz)", -1, freq_hz_start + 1, colno);
350-
constexpr double freq_factor = 1.0 * (1 << 16) * (1 << 16) / 3.5e9;
351-
freq = uint32_t(0.5 + freq_hz * freq_factor);
351+
freq = dds_freq_to_mu(freq_hz);
352352
}
353353
return {chn, freq};
354354
}
@@ -364,7 +364,7 @@ std::pair<uint8_t,uint16_t> ParserBase::read_ampcmd()
364364
auto [ampf, ampf_start] = read_float(0, 1);
365365
if (ampf_start == -1)
366366
syntax_error("Invalid amplitude", colno + 1);
367-
amp = uint16_t(ampf * 4095.0 + 0.5);
367+
amp = dds_amp_to_mu(ampf);
368368
}
369369
return {chn, amp};
370370
}
@@ -417,8 +417,7 @@ std::pair<uint8_t,std::pair<bool,uint16_t>> ParserBase::read_phasecmd()
417417
if (!(abs(phase_deg) <= 360 * 10))
418418
syntax_error("Phase too high (max +-1000%)", -1, phase_deg_start + 1, colno);
419419
phase_deg = fmod(phase_deg, 360);
420-
constexpr double phase_factor = (1 << 14) / 90.0;
421-
phase = uint16_t(0.5 + phase_deg * phase_factor);
420+
phase = dds_phase_to_mu(phase_deg / 360.0);
422421
}
423422
if (neg)
424423
phase = uint16_t(-phase);
@@ -510,7 +509,7 @@ std::pair<bool,std::vector<uint32_t>> ParserBase::read_ttlmask(int max_bank)
510509
auto [ttl_mask, oldcol] = read_hex(0, UINT32_MAX);
511510
if (oldcol < 0)
512511
syntax_error("Expecting hex literal", colno + 1);
513-
ttl_masks.push_back(ttl_mask);
512+
ttl_masks.push_back(uint32_t(ttl_mask));
514513
// Here we need to decide whether we expect a new mask value to be present.
515514
auto c0 = peek();
516515
if (!std::isspace(c0)) {

lib/nacs-seq/zynq/utils.h

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*************************************************************************
2+
* Copyright (c) 2026 - 2026 Yichao Yu <yyc1992@gmail.com> *
3+
* *
4+
* This library is free software; you can redistribute it and/or *
5+
* modify it under the terms of the GNU Lesser General Public *
6+
* License as published by the Free Software Foundation; either *
7+
* version 3.0 of the License, or (at your option) any later version. *
8+
* *
9+
* This library is distributed in the hope that it will be useful, *
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
12+
* Lesser General Public License for more details. *
13+
* *
14+
* You should have received a copy of the GNU Lesser General Public *
15+
* License along with this library. If not, *
16+
* see <http://www.gnu.org/licenses/>. *
17+
*************************************************************************/
18+
19+
#ifndef __NACS_SEQ_ZYNQ_UTILS_H__
20+
#define __NACS_SEQ_ZYNQ_UTILS_H__
21+
22+
#include "../../nacs-utils/number.h"
23+
24+
namespace NaCs::Seq::Zynq {
25+
26+
static inline uint32_t dds_freq_to_mu(double freq)
27+
{
28+
constexpr double factor = 1.0 * (1 << 16) * (1 << 16) / 3.5e9;
29+
if (freq <= 0)
30+
return 0;
31+
// The instruction only have 31 bits so it actually won't encode exactly 1.75 GHz.
32+
if (freq > 1.7499999987776392e9) // last float that would round to 2^31 - 2
33+
return 0x7fffffff;
34+
return round<int32_t>(freq * factor);
35+
}
36+
37+
static constexpr inline double dds_freq_from_mu(uint32_t freq)
38+
{
39+
constexpr double factor = 3.5e9 / (1 << 16) / (1 << 16);
40+
return double(freq) * factor;
41+
}
42+
43+
static inline uint16_t dds_amp_to_mu(double amp)
44+
{
45+
constexpr double factor = 4095;
46+
if (amp <= 0)
47+
return 0;
48+
if (amp >= 1)
49+
return 4095;
50+
return (uint16_t)round<int32_t>(amp * factor);
51+
}
52+
53+
static constexpr inline double dds_amp_from_mu(uint32_t amp)
54+
{
55+
return double(amp) / 4095;
56+
}
57+
58+
static inline uint32_t dds_phase_to_mu(double phase)
59+
{
60+
// phase in [0, 1]
61+
return round<int32_t>(phase * (1 << 16)) & ((1 << 16) - 1);
62+
}
63+
64+
static constexpr inline double dds_phase_from_mu(uint32_t phase)
65+
{
66+
return double(phase) / double(1 << 16);
67+
}
68+
69+
static inline uint32_t dac_to_mu(double V)
70+
{
71+
constexpr double factor = 65535 / 20.0;
72+
constexpr double offset = 10.0;
73+
if (V <= -10)
74+
return 0;
75+
if (V >= 10)
76+
return 0xffff;
77+
V += offset;
78+
return round<int32_t>(V * factor);
79+
}
80+
81+
static constexpr inline double dac_from_mu(uint32_t V)
82+
{
83+
return V * (20.0 / 65535) - 10.0;
84+
}
85+
86+
}
87+
88+
#endif

0 commit comments

Comments
 (0)