Compare commits

...

3 commits

Author SHA1 Message Date
Stephan I. Böttcher
4c36c00e64 python: fpga commands 2026-03-25 20:25:48 +01:00
Stephan I. Böttcher
a88421a781 avr: fpga_cmd fixes 2026-03-25 20:25:15 +01:00
Stephan I. Böttcher
3f9a100e7c fpga spi_slave: longer sclk timeout
The µC needs about 5 µs between bytes.  The 128 mclk timeout @32MHz is
4.3µs.  This commit extends the timeout to 1024 mclk cycles.
2026-03-25 20:20:07 +01:00
6 changed files with 121 additions and 19 deletions

@ -1 +1 @@
Subproject commit 60aae89495f05eedb48c9c1468e4c8d4dd92f79f Subproject commit 9bbfeb3316098af16370c10355c94ed7ec06f5ee

View file

@ -231,3 +231,4 @@ set_global_assignment -name VERILOG_MACRO "L2_AHEPAM=1"
set_global_assignment -name VERILOG_MACRO "WITH_SPI_SSEL=1" set_global_assignment -name VERILOG_MACRO "WITH_SPI_SSEL=1"
set_global_assignment -name VERILOG_MACRO "AX_PORT=1" set_global_assignment -name VERILOG_MACRO "AX_PORT=1"
set_global_assignment -name VERILOG_MACRO "SER_FIFO_ALTERA=1" set_global_assignment -name VERILOG_MACRO "SER_FIFO_ALTERA=1"
set_global_assignment -name VERILOG_MACRO "SPI_TIMEOUT_1024=1"

View file

@ -137,6 +137,6 @@ quartus/thhor_crs.map.rpt: Warning (13040): bidirectional pin "P33[4]" has no
quartus/thhor_crs.map.rpt: Warning (13040): bidirectional pin "P33[5]" has no driver quartus/thhor_crs.map.rpt: Warning (13040): bidirectional pin "P33[5]" has no driver
quartus/thhor_crs.map.rpt: Warning (13040): bidirectional pin "P33[6]" has no driver quartus/thhor_crs.map.rpt: Warning (13040): bidirectional pin "P33[6]" has no driver
quartus/thhor_crs.map.rpt: Warning (13040): bidirectional pin "P33[7]" has no driver quartus/thhor_crs.map.rpt: Warning (13040): bidirectional pin "P33[7]" has no driver
quartus/thhor_crs.map.rpt:Info: Quartus Prime Analysis & Synthesis was successful. 0 errors, 237 warnings quartus/thhor_crs.map.rpt:Info: Quartus Prime Analysis & Synthesis was successful. 0 errors, 238 warnings
quartus/thhor_crs.sta.rpt:Warning (18236): Number of processors has not been specified which may cause overloading on shared machines. Set the global assignment NUM_PARALLEL_PROCESSORS in your QSF to an appropriate value for best performance. quartus/thhor_crs.sta.rpt:Warning (18236): Number of processors has not been specified which may cause overloading on shared machines. Set the global assignment NUM_PARALLEL_PROCESSORS in your QSF to an appropriate value for best performance.
quartus/thhor_crs.sta.rpt:Info: Quartus Prime Timing Analyzer was successful. 0 errors, 1 warning quartus/thhor_crs.sta.rpt:Info: Quartus Prime Timing Analyzer was successful. 0 errors, 1 warning

View file

@ -128,7 +128,7 @@ class dose_cmd(uart.uart):
d = bytes(d) d = bytes(d)
if s is None: if s is None:
s = len(d) s = len(d)
d = struct.pack("<HBB", a,s,ccp) + d + b'\0\0\0\0\0\0\0\0\0\0\0\0' d = struct.pack("<HBB", a,s,ccp) + d + bytes(12)
cc, nn, dd = self.cmd(c, d[:16]) cc, nn, dd = self.cmd(c, d[:16])
if dd and nn: if dd and nn:
aa,ss,cc = struct.unpack("<HBB", dd[:4]) aa,ss,cc = struct.unpack("<HBB", dd[:4])
@ -139,6 +139,9 @@ class dose_cmd(uart.uart):
return name, d return name, d
return cc, nn, dd return cc, nn, dd
def peek_byte(self, a):
return self.peek(a)[-1][0]
def more(self, flags=b''): def more(self, flags=b''):
return self.cmd(self.last_cmd + b'=' + flags) return self.cmd(self.last_cmd + b'=' + flags)
@ -371,6 +374,92 @@ class dose_cmd(uart.uart):
print(f"Power was {("off", "ON")[r[1]]}", file=sys.stderr) print(f"Power was {("off", "ON")[r[1]]}", file=sys.stderr)
return r return r
def fpga_reset(self):
return self.cmd("C@")
FPGA_FLGS = {
"ABORT": 0x01,
"ABORTED": 0x10,
"BUSY": 0x20,
"SUBMITTED": 0x40,
"CONFIG": 0x80,
"NOOP": 0x8000,
}
def fpga_cmd(self, c, read=0, wait=0, flags=()):
n = len(c) & 0xe
if n != len(c):
raise ValueError(f"FPGA command must be even 2…14 bytes {c!r}")
n |= flags2int(self.FPGA_FLGS, flags)
z = read << 1
if wait:
if wait > 7 or z > 14:
raise ValueError("FPGA cannot wait-read more than 14 bytes")
z |= wait << 4
z |= 1
z |= n >> 8
n &= 0xff
c = bytes((n, z)) + c + bytes(14)
r, e, d = self.cmd("C", c[:16])
s = int2flags(self.FPGA_FLGS, d[0])
print(f"FPGA cmd status {s!r}", file=sys.stderr)
return d
def fpga_config(self, filename="../fpga/quartus/thhor_crs.rbf"):
self.fpga_reset()
n = 0
nn = 0
with open(filename, "rb") as f:
c = f.read(14)
while c:
if len(c) & 1:
c += b'\0'
d = fpga_cmd(c, flags="CONFIG")
if d[0] & self.FPGA_FLGS["SUBMITTED"]:
n += len(c)
c = f.read(14)
if n > nn + 1024:
if ~self.peek_byte("VPORTA_IN") & 0x10:
raise FPGA_Error("nSTATUS went low during config")
print(f"{n} bytes sent", file=sys.stderr)
nn += 1024
return n
def icmd(self, c, p=None, r=2):
w = [c | 0x8000]
if p is not None:
w[0] |= 0x4000
w.append(p)
if r:
w.extend([0x8001]*(r-1))
w.append(0)
d = struct.pack(f">{len(w)}H", *w)
print(f"icmd → {[f"{ww:04x}" for ww in w]}")
d = self.fpga_cmd(d)
r = struct.unpack(f">{len(w)+1}H", d[:2*len(w)+2])
print(f"{[f"{rr:04x}" for rr in r]}")
return r
def flags2int(FLAGS, flags):
r = 0
if isinstance(flags, int):
return flags
if isinstance(flags, str):
return FLAGS[flags]
for f in flags:
if isinstance(f, int):
r |= f
else:
r |= FLAGS[f]
return r
def int2flags(FLAGS, flags):
r = []
for f in FLAGS:
if flags & FLAGS[f]:
r.append(f)
return r
if tty: if tty:
tty = dose_cmd(tty, baud) tty = dose_cmd(tty, baud)
tty._export(globals()) tty._export(globals())

View file

@ -22,36 +22,37 @@ void fpga_reset()
void fpga_cmd(struct fpga_cmd *c) void fpga_cmd(struct fpga_cmd *c)
{ {
uint8_t n = c->n;
uint8_t z = c->z;
if (fpga_reset_poll()) { if (fpga_reset_poll()) {
c->n |= 0x20; // busy flag c->n = n | fpga_busy;
return; return;
} }
if (c->n & 1 && spi_abort()) { if (n & fpga_abort && spi_abort()) {
c->n |= 0x10; // aborted flag c->n = n | fpga_aborted;
return; return;
} }
else if (spi_busy_p()) { else if (spi_busy_p()) {
c->n |= 0x20; // busy flag c->n = n | fpga_busy;
return; return;
} }
c->n &=~ 0x10; // not busy c->n = n & ~fpga_busy;
if (c->n & 0x40) if (n & fpga_submitted)
return; return;
c->n |= 0x40; // submitted flag c->n = n | fpga_submitted;
uint8_t n = c->n & 0x0e; // send up to seven cmd words
uint8_t z = c->z & 0x7e; // and up to 63 zeros spi_select(n & fpga_config ? SPI_CONFIG : 0);
spi_select(0); spi.csize = n &= fpga_size;
spi.zero = c->n & 0x80; // send nop: 0x8080 (please), or zeros spi.zero = z & 0x80; // send 0x0000 or 0x8080
spi.csize = n;
spi.cmd = c->d; spi.cmd = c->d;
spi.rdata = c->d; spi.rdata = c->d;
if (c->z & 0x80) { if (z & fpga_wait_nonzero) {
spi.isize = n + z>>3 & 0x0e; // ignore cmd ± (z[6:4]) spi.isize = n + (z>>3 & fpga_size); // ignore cmd ± (z[6:4])
spi.zsize = spi.rsize = z & 0x0e; // read x[3:1] words spi.zsize = spi.rsize = z & fpga_size; // read x[3:1] words
spi.mask = 0xff; // start reading at the first nonzero byte after cmd spi.mask = 0xff; // start reading at the first nonzero byte after cmd
} }
else { else {
spi.zsize = z; // send z zeros/nop after cmd spi.zsize = z &= 0x7e; // send z zeros/nop after cmd
spi.rsize = 14; // save the last 7 words returned spi.rsize = 14; // save the last 7 words returned
if (n + z > 14) if (n + z > 14)
spi.isize = n + z - 14; spi.isize = n + z - 14;

View file

@ -7,6 +7,17 @@ struct fpga_cmd {
uint8_t d[14]; uint8_t d[14];
}; };
enum fpga_flags {
fpga_abort = 0x01,
fpga_size = 0x0e,
fpga_aborted = 0x10,
fpga_busy = 0x20,
fpga_submitted = 0x40,
fpga_config = 0x80,
fpga_wait_nonzero = 0x01,
};
void fpga_reset(); void fpga_reset();
uint8_t fpga_reset_poll(); uint8_t fpga_reset_poll();
void fpga_cmd(struct fpga_cmd *c); void fpga_cmd(struct fpga_cmd *c);