1use std::cmp;
6use std::collections::HashMap;
7use std::convert::{TryFrom, TryInto};
8use std::marker::PhantomData;
9use std::sync::LazyLock;
10use std::time::Duration;
11
12use anyhow::{Context, Result, ensure};
13
14use super::board::Board;
15use crate::collection;
16use crate::io::gpio::GpioError;
17use crate::io::spi::SpiError;
18use crate::transport::{ProgressIndicator, TransportError, TransportInterfaceType};
19use crate::util::parse_int::ParseInt;
20use crate::util::usb::UsbBackend;
21
22pub struct Backend<B: Board> {
24 usb: UsbBackend,
25 _marker: PhantomData<B>,
26}
27
28#[derive(Default, Debug, Clone)]
30struct PllMulDiv {
31 numerator: u16,
32 denominator: u16,
33 outdiv: u8,
34 fvco: u32,
35}
36
37#[derive(Ord, PartialOrd, Eq, PartialEq, Debug, Clone, serde::Serialize)]
38pub struct FirmwareVersion(u8, u8, u8);
39
40impl<B: Board> Backend<B> {
41 pub const CMD_FW_VERSION: u8 = 0x17;
43 pub const CMD_CDC_SETTINGS_EN: u8 = 0x31;
44 pub const CMD_READMEM_BULK: u8 = 0x10;
45 pub const CMD_WRITEMEM_BULK: u8 = 0x11;
46 pub const CMD_READMEM_CTRL: u8 = 0x12;
47 pub const CMD_WRITEMEM_CTRL: u8 = 0x13;
48 pub const CMD_MEMSTREAM: u8 = 0x14;
49 pub const CMD_WRITEMEM_CTRL_SAM3U: u8 = 0x15;
50 pub const CMD_SMC_READ_SPEED: u8 = 0x27;
51 pub const CMD_FW_BUILD_DATE: u8 = 0x40;
52
53 pub const CMD_SAM3X_CFG: u8 = 0x22;
55 pub const SAM3X_RESET: u16 = 0x10;
56
57 pub const CMD_PLL: u8 = 0x30;
58 pub const REQ_PLL_WRITE: u8 = 0x01;
59 pub const REQ_PLL_READ: u8 = 0x00;
60 pub const RESP_PLL_OK: u8 = 0x02;
61 pub const ADDR_PLL_ENABLE: u8 = 0x0c;
62
63 pub const CMD_FPGAIO_UTIL: u8 = 0x34;
66 pub const REQ_IO_CONFIG: u16 = 0xA0;
68 pub const REQ_IO_RELEASE: u16 = 0xA1;
69 pub const REQ_IO_OUTPUT: u16 = 0xA2;
70 pub const CONFIG_PIN_INPUT: u8 = 0x01;
72 pub const CONFIG_PIN_OUTPUT: u8 = 0x02;
73 pub const CONFIG_PIN_SPI1_SDO: u8 = 0x10;
74 pub const CONFIG_PIN_SPI1_SDI: u8 = 0x11;
75 pub const CONFIG_PIN_SPI1_SCK: u8 = 0x12;
76 pub const CONFIG_PIN_SPI1_CS: u8 = 0x13;
77
78 pub const CMD_FPGASPI1_XFER: u8 = 0x35;
81 pub const REQ_ENABLE_SPI: u16 = 0xA0;
82 pub const REQ_DISABLE_SPI: u16 = 0xA1;
83 pub const REQ_CS_LOW: u16 = 0xA2;
84 pub const REQ_CS_HIGH: u16 = 0xA3;
85 pub const REQ_SEND_DATA: u16 = 0xA4;
86
87 pub const FPGA_PROG_SPEED: u32 = 20_000_000;
89
90 pub const CMD_FPGA_STATUS: u8 = 0x15;
92 pub const CMD_FPGA_PROGRAM: u8 = 0x16;
93 pub const PROGRAM_INIT: u16 = 0xA0;
96 pub const PROGRAM_PREPARE: u16 = 0xA1;
97 pub const PROGRAM_EXIT: u16 = 0xA2;
98
99 pub const BULK_IN_EP: u8 = 0x81;
101 pub const BULK_OUT_EP: u8 = 0x02;
102
103 const LAST_PIN_NUMBER: u8 = 106;
104
105 pub fn new(
107 usb_vid: Option<u16>,
108 usb_pid: Option<u16>,
109 usb_serial: Option<&str>,
110 ) -> Result<Self> {
111 Ok(Backend {
112 usb: UsbBackend::new(
113 usb_vid.unwrap_or(B::VENDOR_ID),
114 usb_pid.unwrap_or(B::PRODUCT_ID),
115 usb_serial,
116 )?,
117 _marker: PhantomData,
118 })
119 }
120
121 pub fn send_ctrl(&self, cmd: u8, value: u16, data: &[u8]) -> Result<usize> {
123 log::debug!(
124 "WRITE_CTRL: bmRequestType: {:02x}, bRequest: {:02x}, wValue: {:04x}, wIndex: {:04x}, data: {:?}",
125 0x41,
126 cmd,
127 value,
128 0,
129 data
130 );
131 self.usb.write_control(0x41, cmd, value, 0, data)
132 }
133
134 pub fn read_ctrl(&self, cmd: u8, value: u16, data: &mut [u8]) -> Result<usize> {
136 log::debug!(
137 "READ_CTRL: bmRequestType: {:02x}, bRequest: {:02x}, wValue: {:04x}, wIndex: {:04x}, data: {:?}",
138 0xC1,
139 cmd,
140 value,
141 0,
142 data
143 );
144 self.usb.read_control(0xC1, cmd, value, 0, data)
145 }
146
147 pub fn get_serial_number(&self) -> &str {
149 self.usb.get_serial_number()
150 }
151
152 pub fn get_firmware_build_date(&self) -> Result<String> {
154 let mut buf = [0u8; 100];
155 let len = self.read_ctrl(Backend::<B>::CMD_FW_BUILD_DATE, 0, &mut buf)?;
156 Ok(String::from_utf8_lossy(&buf[0..len]).to_string())
157 }
158
159 pub fn get_firmware_version(&self) -> Result<FirmwareVersion> {
161 let mut buf = [0u8; 3];
162 self.read_ctrl(Backend::<B>::CMD_FW_VERSION, 0, &mut buf)?;
163 Ok(FirmwareVersion(buf[0], buf[1], buf[2]))
164 }
165
166 pub fn pin_set_output(&self, pinname: &str, output: bool) -> Result<()> {
168 let pinnum = Backend::<B>::pin_name_to_number(pinname)?;
169 self.send_ctrl(
170 Backend::<B>::CMD_FPGAIO_UTIL,
171 Backend::<B>::REQ_IO_CONFIG,
172 &[
173 pinnum,
174 if output {
175 Backend::<B>::CONFIG_PIN_OUTPUT
176 } else {
177 Backend::<B>::CONFIG_PIN_INPUT
178 },
179 ],
180 )?;
181 Ok(())
182 }
183
184 pub fn pin_get_state(&self, pinname: &str) -> Result<u8> {
186 let pinnum = Backend::<B>::pin_name_to_number(pinname)
187 .ok()
188 .ok_or_else(|| {
189 TransportError::InvalidInstance(TransportInterfaceType::Gpio, pinname.to_string())
190 })? as u16;
191 let mut buf = [0u8; 1];
192 self.read_ctrl(Backend::<B>::CMD_FPGAIO_UTIL, pinnum, &mut buf)
193 .context("USB error")?;
194 Ok(buf[0])
195 }
196
197 pub fn pin_set_state(&self, pinname: &str, value: bool) -> Result<()> {
199 let pinnum = Backend::<B>::pin_name_to_number(pinname)?;
200 self.send_ctrl(
201 Backend::<B>::CMD_FPGAIO_UTIL,
202 Backend::<B>::REQ_IO_OUTPUT,
203 &[pinnum, value as u8],
204 )?;
205 Ok(())
206 }
207
208 pub fn reset_sam3x(&self) -> Result<()> {
211 self.send_ctrl(Backend::<B>::CMD_SAM3X_CFG, Backend::<B>::SAM3X_RESET, &[])?;
212 Ok(())
213 }
214
215 pub fn spi1_setpins(&self, sdo: &str, sdi: &str, sck: &str, cs: &str) -> Result<()> {
217 let sdo = Backend::<B>::pin_name_to_number(sdo)?;
218 let sdi = Backend::<B>::pin_name_to_number(sdi)?;
219 let sck = Backend::<B>::pin_name_to_number(sck)?;
220 let cs = Backend::<B>::pin_name_to_number(cs)?;
221
222 self.send_ctrl(
223 Backend::<B>::CMD_FPGAIO_UTIL,
224 Backend::<B>::REQ_IO_CONFIG,
225 &[sdo, Backend::<B>::CONFIG_PIN_SPI1_SDO],
226 )?;
227 self.send_ctrl(
228 Backend::<B>::CMD_FPGAIO_UTIL,
229 Backend::<B>::REQ_IO_CONFIG,
230 &[sdi, Backend::<B>::CONFIG_PIN_SPI1_SDI],
231 )?;
232 self.send_ctrl(
233 Backend::<B>::CMD_FPGAIO_UTIL,
234 Backend::<B>::REQ_IO_CONFIG,
235 &[sck, Backend::<B>::CONFIG_PIN_SPI1_SCK],
236 )?;
237 self.send_ctrl(
238 Backend::<B>::CMD_FPGAIO_UTIL,
239 Backend::<B>::REQ_IO_CONFIG,
240 &[cs, Backend::<B>::CONFIG_PIN_SPI1_CS],
241 )?;
242 Ok(())
243 }
244
245 pub fn spi1_enable(&self, enable: bool) -> Result<()> {
247 self.send_ctrl(
248 Backend::<B>::CMD_FPGASPI1_XFER,
249 if enable {
250 Backend::<B>::REQ_ENABLE_SPI
251 } else {
252 Backend::<B>::REQ_DISABLE_SPI
253 },
254 &[],
255 )?;
256 Ok(())
257 }
258
259 pub fn spi1_set_cs_pin(&self, status: bool) -> Result<()> {
261 self.send_ctrl(
262 Backend::<B>::CMD_FPGASPI1_XFER,
263 if status {
264 Backend::<B>::REQ_CS_HIGH
265 } else {
266 Backend::<B>::REQ_CS_LOW
267 },
268 &[],
269 )?;
270 Ok(())
271 }
272
273 pub fn spi1_tx_rx(&self, txdata: &[u8], rxdata: &mut [u8]) -> Result<()> {
276 ensure!(
277 txdata.len() <= 64,
278 SpiError::InvalidDataLength(txdata.len())
279 );
280 ensure!(
281 rxdata.len() <= 64,
282 SpiError::InvalidDataLength(rxdata.len())
283 );
284 ensure!(
285 txdata.len() == rxdata.len(),
286 SpiError::MismatchedDataLength(txdata.len(), rxdata.len())
287 );
288 self.send_ctrl(
289 Backend::<B>::CMD_FPGASPI1_XFER,
290 Backend::<B>::REQ_SEND_DATA,
291 txdata,
292 )?;
293 self.read_ctrl(Backend::<B>::CMD_FPGASPI1_XFER, 0, rxdata)?;
294 Ok(())
295 }
296
297 pub fn spi1_read(&self, buffer: &mut [u8]) -> Result<()> {
300 let wbuf = [0u8; 64];
301 for chunk in buffer.chunks_mut(64) {
302 self.spi1_tx_rx(&wbuf[..chunk.len()], chunk)?;
303 }
304 Ok(())
305 }
306
307 pub fn spi1_write(&self, buffer: &[u8]) -> Result<()> {
310 let mut rbuf = [0u8; 64];
311 for chunk in buffer.chunks(64) {
312 self.spi1_tx_rx(chunk, &mut rbuf[..chunk.len()])?;
313 }
314 Ok(())
315 }
316
317 pub fn spi1_both(&self, txbuf: &[u8], rxbuf: &mut [u8]) -> Result<()> {
320 ensure!(
321 txbuf.len() == rxbuf.len(),
322 SpiError::MismatchedDataLength(txbuf.len(), rxbuf.len())
323 );
324 for (wchunk, rchunk) in txbuf.chunks(64).zip(rxbuf.chunks_mut(64)) {
325 self.spi1_tx_rx(wchunk, rchunk)?;
326 }
327 Ok(())
328 }
329
330 pub fn fpga_is_programmed(&self) -> Result<bool> {
332 let mut status = [0u8; 4];
333 self.read_ctrl(Backend::<B>::CMD_FPGA_STATUS, 0, &mut status)?;
334 Ok(status[0] & 0x01 != 0)
335 }
336
337 fn fpga_prepare(&self, speed_hz: u32) -> Result<()> {
339 let supports_variable_speed = self.get_firmware_version()? >= FirmwareVersion(1, 0, 0);
340 let speed_hz = speed_hz.to_le_bytes();
341 self.send_ctrl(
342 Backend::<B>::CMD_FPGA_PROGRAM,
343 Backend::<B>::PROGRAM_INIT,
344 if supports_variable_speed {
345 &speed_hz
346 } else {
347 &[]
348 },
349 )?;
350 std::thread::sleep(Duration::from_millis(1));
351 self.send_ctrl(
352 Backend::<B>::CMD_FPGA_PROGRAM,
353 Backend::<B>::PROGRAM_PREPARE,
354 &[],
355 )?;
356 std::thread::sleep(Duration::from_millis(1));
357 Ok(())
358 }
359
360 fn fpga_download(&self, bitstream: &[u8], progress: &dyn ProgressIndicator) -> Result<()> {
361 let mut stream = bitstream[0x7C..].to_vec();
365
366 let newlen = stream.len()
370 + if stream.len().is_multiple_of(32) {
371 33
372 } else {
373 32
374 };
375 stream.resize(newlen, 0xFF);
376
377 progress.new_stage("", stream.len());
378
379 const CHUNK_LEN: usize = 2048;
382 for (chunk_no, chunk) in stream.chunks(CHUNK_LEN).enumerate() {
383 progress.progress(CHUNK_LEN * chunk_no);
384 self.usb.write_bulk(Backend::<B>::BULK_OUT_EP, chunk)?;
385 }
386 progress.progress(stream.len());
387 Ok(())
388 }
389
390 pub fn fpga_program(&self, bitstream: &[u8], progress: &dyn ProgressIndicator) -> Result<()> {
392 self.fpga_prepare(Backend::<B>::FPGA_PROG_SPEED)?;
393 let result = self.fpga_download(bitstream, progress);
394
395 let mut status = false;
396 if result.is_ok() {
397 for _ in 0..5 {
398 status = self.fpga_is_programmed()?;
399 if status {
400 break;
401 }
402 std::thread::sleep(Duration::from_millis(1));
403 }
404 }
405 self.send_ctrl(
406 Backend::<B>::CMD_FPGA_PROGRAM,
407 Backend::<B>::PROGRAM_EXIT,
408 &[],
409 )?;
410
411 if let Err(e) = result {
412 Err(TransportError::FpgaProgramFailed(e.to_string()).into())
413 } else if !status {
414 Err(TransportError::FpgaProgramFailed("unknown error".to_string()).into())
415 } else {
416 Ok(())
417 }
418 }
419
420 pub fn clear_bitstream(&self) -> Result<()> {
421 self.fpga_prepare(Backend::<B>::FPGA_PROG_SPEED)?;
422 self.send_ctrl(
423 Backend::<B>::CMD_FPGA_PROGRAM,
424 Backend::<B>::PROGRAM_EXIT,
425 &[],
426 )?;
427 if self.fpga_is_programmed()? {
428 Err(TransportError::ClearBitstreamFailed().into())
429 } else {
430 Ok(())
431 }
432 }
433
434 pub fn pin_name_to_number(pinname: &str) -> Result<u8> {
436 if let Ok(pinnum) = u8::from_str(pinname) {
439 ensure!(
440 pinnum <= Backend::<B>::LAST_PIN_NUMBER,
441 GpioError::InvalidPinNumber(pinnum)
442 );
443 return Ok(pinnum);
444 }
445 let pinname = pinname.to_uppercase();
446 let pn = pinname.as_str();
447
448 if SCHEMATIC_PIN_NAMES.contains_key(pn) {
449 Ok(SAM3X_PIN_NAMES[SCHEMATIC_PIN_NAMES[pn]])
450 } else if SAM3X_PIN_NAMES.contains_key(pn) {
451 Ok(SAM3X_PIN_NAMES[pn])
452 } else {
453 Err(GpioError::InvalidPinName(pinname).into())
454 }
455 }
456
457 fn pll_write(&self, addr: u8, data: u8) -> Result<()> {
459 if self.pll_read(addr)? == data {
462 log::debug!(
463 "Skipping PLL write since address {} is already {}",
464 addr,
465 data
466 );
467 return Ok(());
468 }
469 self.send_ctrl(
470 Backend::<B>::CMD_PLL,
471 0,
472 &[Backend::<B>::REQ_PLL_WRITE, addr, data],
473 )?;
474 let mut resp = [0u8; 2];
475 self.read_ctrl(Backend::<B>::CMD_PLL, 0, &mut resp)?;
476 if resp[0] != Backend::<B>::RESP_PLL_OK {
477 Err(
478 TransportError::PllProgramFailed(format!("CDCE906 write error: {}", resp[0]))
479 .into(),
480 )
481 } else {
482 Ok(())
483 }
484 }
485
486 fn pll_read(&self, addr: u8) -> Result<u8> {
488 self.send_ctrl(
489 Backend::<B>::CMD_PLL,
490 0,
491 &[Backend::<B>::REQ_PLL_READ, addr, 0],
492 )?;
493 let mut resp = [0u8; 2];
494 self.read_ctrl(Backend::<B>::CMD_PLL, 0, &mut resp)?;
495 if resp[0] != Backend::<B>::RESP_PLL_OK {
496 Err(TransportError::PllProgramFailed(format!("CDCE906 read error: {}", resp[0])).into())
497 } else {
498 Ok(resp[1])
499 }
500 }
501
502 pub fn pll_enable(&self, enable: bool) -> Result<()> {
504 let mut reg = self.pll_read(12)?;
506 if enable {
507 reg &= !(1 << 6);
508 } else {
509 reg |= 1 << 6;
510 }
511 self.pll_write(12, reg)
512 }
513
514 fn pll_calc_mul_div(&self, target_freq: u32) -> Result<PllMulDiv> {
516 const TARGET_FREQ_MIN: u32 = 630_000;
517 const TARGET_FREQ_MAX: u32 = 167_000_000;
518 if !(TARGET_FREQ_MIN..=TARGET_FREQ_MAX).contains(&target_freq) {
519 return Err(TransportError::PllProgramFailed(format!(
520 "Target frequency out of range: {}",
521 target_freq
522 ))
523 .into());
524 }
525
526 const REF_FREQ: u32 = 12_000_000;
527 const FVCO_MIN: u32 = 80_000_000;
528 const FVCO_MAX: u32 = 300_000_000;
529 let mut res = PllMulDiv::default();
530 let outdiv_min: u8 = cmp::max(FVCO_MIN / target_freq, 1u32).try_into()?;
532 let outdiv_max: u8 = cmp::min(FVCO_MAX / target_freq, 127u32).try_into()?;
533 let mut best_err: u64 = u64::MAX;
534
535 'outer: for outdiv in outdiv_min..=outdiv_max {
536 let fvco_exp = target_freq as u64 * outdiv as u64;
537 for numerator in 1u16..4096 {
538 for denominator in 1u16..512 {
539 let fvco_act = (REF_FREQ as u64 * numerator as u64) / denominator as u64;
540 let err = fvco_exp.abs_diff(fvco_act);
541 if err < best_err {
542 best_err = err;
543 res = PllMulDiv {
544 numerator,
545 denominator,
546 outdiv,
547 fvco: fvco_act.try_into()?,
548 };
549 }
550 if best_err == 0 {
551 break 'outer;
552 }
553 }
554 }
555 }
556
557 if !(FVCO_MIN..=FVCO_MAX).contains(&res.fvco) {
558 Err(
559 TransportError::PllProgramFailed(format!("fvco value out of range: {}", res.fvco))
560 .into(),
561 )
562 } else {
563 Ok(res)
564 }
565 }
566
567 pub fn pll_out_freq_set(&self, pll_num: u8, target_freq: u32) -> Result<()> {
569 if pll_num > 2 {
570 return Err(
571 TransportError::PllProgramFailed(format!("Unknown PLL: {}", pll_num)).into(),
572 );
573 }
574
575 let vals = self.pll_calc_mul_div(target_freq)?;
577 log::debug!(
578 "target_freq: {}, vals: {:?}, error: {}",
579 target_freq,
580 vals,
581 vals.fvco / u32::from(vals.outdiv) - target_freq
582 );
583 let offset = 3 * pll_num;
585 self.pll_write(1 + offset, (vals.denominator & 0xff).try_into()?)?;
586 self.pll_write(2 + offset, (vals.numerator & 0xff).try_into()?)?;
587 let mut base = self.pll_read(3 + offset)?;
588 base &= 0xe0;
589 base |= u8::try_from((vals.denominator & 0x100) >> 8)?;
590 base |= u8::try_from((vals.numerator & 0xf00) >> 7)?;
591 self.pll_write(3 + offset, base)?;
592 self.pll_write(13 + pll_num, vals.outdiv & 0x7f)?;
593
594 const FVCO_HIGH_SPEED: u32 = 180_000_000;
596 let mut data = self.pll_read(6)?;
597 let pll_bit = match pll_num {
598 0 => 7,
599 1 => 6,
600 2 => 5,
601 _ => {
602 return Err(
603 TransportError::PllProgramFailed(format!("Unknown PLL: {}", pll_num)).into(),
604 );
605 }
606 };
607 data &= !(1 << pll_bit);
608 if vals.fvco > FVCO_HIGH_SPEED {
609 data |= 1 << pll_bit;
610 }
611 self.pll_write(6, data)
612 }
613
614 pub fn pll_out_enable(&self, pll_num: u8, enable: bool) -> Result<()> {
616 const SLEW_RATE: u8 = 3;
618 let (offset, div_src) = match pll_num {
619 0 => (0, 0),
620 1 => (1, 1),
621 2 => (4, 2),
622 _ => {
623 return Err(
624 TransportError::PllProgramFailed(format!("Unknown PLL: {}", pll_num)).into(),
625 );
626 }
627 };
628
629 let mut data = 0;
631 if enable {
632 data |= 1 << 3;
633 }
634 data |= div_src;
635 data |= SLEW_RATE << 4;
636 self.pll_write(19 + offset, data)?;
637
638 Ok(())
639 }
640
641 pub fn pll_write_defaults(&self) -> Result<()> {
643 let data = self.pll_read(26)?;
645 self.pll_write(26, data | (1 << 7))?;
646
647 while self.pll_read(24)? & (1 << 7) != 0 {
648 std::thread::sleep(Duration::from_millis(50));
649 }
650
651 self.pll_write(26, data & !(1 << 7))
652 }
653}
654
655static SAM3X_PIN_NAMES: LazyLock<HashMap<&'static str, u8>> = LazyLock::new(|| {
657 collection! {
658 "PA0" => 0,
659 "PA1" => 1,
660 "PA2" => 2,
661 "PA3" => 3,
662 "PA4" => 4,
663 "PA5" => 5,
664 "PA6" => 6,
665 "PA7" => 7,
666 "PA8" => 8,
667 "PA9" => 9,
668 "PA10" => 10,
669 "PA11" => 11,
670 "PA12" => 12,
671 "PA13" => 13,
672 "PA14" => 14,
673 "PA15" => 15,
674 "PA16" => 16,
675 "PA17" => 17,
676 "PA18" => 18,
677 "PA19" => 19,
678 "PA20" => 20,
679 "PA21" => 21,
680 "PA22" => 22,
681 "PA23" => 23,
682 "PA24" => 24,
683 "PA25" => 25,
684 "PA26" => 26,
685 "PA27" => 27,
686 "PA28" => 28,
687 "PA29" => 29,
688 "PB0" => 32,
689 "PB1" => 33,
690 "PB2" => 34,
691 "PB3" => 35,
692 "PB4" => 36,
693 "PB5" => 37,
694 "PB6" => 38,
695 "PB7" => 39,
696 "PB8" => 40,
697 "PB9" => 41,
698 "PB10" => 42,
699 "PB11" => 43,
700 "PB12" => 44,
701 "PB13" => 45,
702 "PB14" => 46,
703 "PB15" => 47,
704 "PB16" => 48,
705 "PB17" => 49,
706 "PB18" => 50,
707 "PB19" => 51,
708 "PB20" => 52,
709 "PB21" => 53,
710 "PB22" => 54,
711 "PB23" => 55,
712 "PB24" => 56,
713 "PB25" => 57,
714 "PB26" => 58,
715 "PB27" => 59,
716 "PB28" => 60,
717 "PB29" => 61,
718 "PB30" => 62,
719 "PB31" => 63,
720 "PC0" => 64,
721 "PC1" => 65,
722 "PC2" => 66,
723 "PC3" => 67,
724 "PC4" => 68,
725 "PC5" => 69,
726 "PC6" => 70,
727 "PC7" => 71,
728 "PC8" => 72,
729 "PC9" => 73,
730 "PC10" => 74,
731 "PC11" => 75,
732 "PC12" => 76,
733 "PC13" => 77,
734 "PC14" => 78,
735 "PC15" => 79,
736 "PC16" => 80,
737 "PC17" => 81,
738 "PC18" => 82,
739 "PC19" => 83,
740 "PC20" => 84,
741 "PC21" => 85,
742 "PC22" => 86,
743 "PC23" => 87,
744 "PC24" => 88,
745 "PC25" => 89,
746 "PC26" => 90,
747 "PC27" => 91,
748 "PC28" => 92,
749 "PC29" => 93,
750 "PC30" => 94,
751 "PD0" => 96,
752 "PD1" => 97,
753 "PD2" => 98,
754 "PD3" => 99,
755 "PD4" => 100,
756 "PD5" => 101,
757 "PD6" => 102,
758 "PD7" => 103,
759 "PD8" => 104,
760 "PD9" => 105,
761 "PD10" => 106
762 }
763});
764static SCHEMATIC_PIN_NAMES: LazyLock<HashMap<&'static str, &'static str>> = LazyLock::new(|| {
766 collection! {
767 "USBSPARE0" => "PC10",
768 "USBSPARE1" => "PC11",
769 "USBSPARE2" => "PC12",
770 "USBSPARE3" => "PC13",
771 "USBRD" => "PA29",
772 "USBWR" => "PC18",
773 "USBCE" => "PA6",
774 "USBALE" => "PC17",
775 "USBCK0" => "PB22",
776 "USBCK1" => "PA24",
777 "USB_A0" => "PC21",
778 "USB_A1" => "PC22",
779 "USB_A2" => "PC23",
780 "USB_A3" => "PC24",
781 "USB_A4" => "PC25",
782 "USB_A5" => "PC26",
783 "USB_A6" => "PC27",
784 "USB_A7" => "PC28",
785 "USB_A8" => "PC29",
786 "USB_A9" => "PC30",
787 "USB_A10" => "PD0",
788 "USB_A11" => "PD1",
789 "USB_A12" => "PD2",
790 "USB_A13" => "PD3",
791 "USB_A14" => "PD4",
792 "USB_A15" => "PD5",
793 "USB_A16" => "PD6",
794 "USB_A17" => "PD7",
795 "USB_A18" => "PD8",
796 "USB_A19" => "PD9",
797 "USB_D0" => "PC2",
798 "USB_D1" => "PC3",
799 "USB_D2" => "PC4",
800 "USB_D3" => "PC5",
801 "USB_D4" => "PC6",
802 "USB_D5" => "PC7",
803 "USB_D6" => "PC8",
804 "USB_D7" => "PC9",
805 "SWSTATE" => "PB26",
806 "PWRON" => "PB27",
807 "LEDSURGE" => "PB14",
808 "SAM_FPGA_CFG_CS" => "PB16",
809 "CFG_INITB" => "PB18",
810 "CFG_DONE" => "PB17",
811 "CFB_PROGRAMB" => "PB19",
812 "SAM_FPGA_COPI" => "PB20",
813 "SAM_FPGA_CIPO" => "PB21",
814 "SAM_FPGA_CCLK" => "PB24",
815 "USB_CLK1" => "PA24",
816 "USB_SPI_CIPO" => "PA25",
817 "USB_SPI_COPI" => "PA26",
818 "USB_SPI_SCK" => "PA27",
819 "USB_SPI_CS" => "PA28"
820 }
821});