ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
board_periph_usbhs_host.c
Go to the documentation of this file.
1
43
44#include <stdint.h>
45#include <stdio.h>
46#include <string.h>
47
48#include "board_periph_block.h"
49#include "board_usb.h"
51#include "ra8_usb_regs.h"
52
53/* =============================================================================
54 * Window geometry + model sizing.
55 * =============================================================================
56 */
57
59typedef enum : uint64_t {
60 k_usbhs_base = 0x40351000UL,
61 k_usbhs_span = 0x200UL,
62 k_usbhs_words = 0x200UL / 2U,
63 k_usbhs_dev_span = 0x100UL,
70
71/* =============================================================================
72 * Host-role register bit/field values not carried by ra8_usb_regs.h.
73 * =============================================================================
74 */
75
77typedef enum : uint16_t {
78 k_usbhs_lnst_j = 0x0001U,
80 k_usbhs_rhst_fs = 0x0002U,
83
85typedef enum : uint8_t {
88
90typedef enum : uint16_t {
91 k_usbhs_plllock = 0x0001U,
93
95typedef enum : uint16_t {
96 k_usbhs_dcp_bit = 0x0001U,
98
105
114
126
127/* =============================================================================
128 * Model state.
129 * =============================================================================
130 */
131
173
175
176/* =============================================================================
177 * Small register / pipe helpers (defined before the dispatchers that use them).
178 * =============================================================================
179 */
180
191RA8_INTERNAL static uint32_t internal_hs_word(uint64_t off)
192{
193 return (uint32_t)((off & ~(uint64_t)1U) / 2U);
194}
195
206RA8_INTERNAL static uint16_t internal_hs_reg(uint16_t off)
207{
208 return s_hs.reg[internal_hs_word((uint64_t)off)];
209}
210
220RA8_INTERNAL static void internal_hs_set(uint16_t off, uint16_t value)
221{
222 s_hs.reg[internal_hs_word((uint64_t)off)] = value;
223}
224
234RA8_INTERNAL static void internal_hs_or(uint16_t off, uint16_t bits)
235{
236 internal_hs_set(off, (uint16_t)(internal_hs_reg(off) | bits));
237}
238
249{
250 const uint16_t sel =
252 return (uint8_t)(sel % (uint8_t)k_usbhs_pipes);
253}
254
265{
266 return (uint8_t)(internal_hs_reg((uint16_t)k_ra8_usb_off_pipesel) % (uint8_t)k_usbhs_pipes);
267}
268
279RA8_INTERNAL static bool internal_hs_is_pipectr(uint64_t off)
280{
281 const uint64_t lo = (uint64_t)k_ra8_usb_off_pipectr;
282 const uint64_t hi = lo + ((uint64_t)k_ra8_usb_pipectr_count * 2U);
283 return (off >= lo) && (off < hi);
284}
285
296RA8_INTERNAL static uint8_t internal_hs_pipectr_index(uint64_t off)
297{
298 const uint8_t p = (uint8_t)(((off - (uint64_t)k_ra8_usb_off_pipectr) / 2U) + 1U);
299 return (uint8_t)(p % (uint8_t)k_usbhs_pipes);
300}
301
312{
313 const uint8_t p = internal_hs_curpipe();
314 if (p == 0U) {
315 return (uint16_t)(s_hs.din_len - s_hs.din_rd);
316 }
317 return (uint16_t)(s_hs.pin_len[p] - s_hs.pin_rd[p]);
318}
319
320/* =============================================================================
321 * CFIFO data port -- move payload bytes between the host and the staging buffers.
322 * =============================================================================
323 */
324
334RA8_INTERNAL static void internal_hs_cfifo_write(uint32_t value, unsigned size)
335{
336 const uint8_t p = internal_hs_curpipe();
337 for (unsigned i = 0U; (i < size) && (s_hs.pout_len[p] < (uint16_t)k_usbhs_pkt_cap); i++) {
338 s_hs.pout[p][s_hs.pout_len[p]] =
339 (uint8_t)((value >> (i * (unsigned)k_usbhs_byte_bits)) & (uint32_t)k_usbhs_byte_mask);
340 s_hs.pout_len[p]++;
341 }
342}
343
354RA8_INTERNAL static uint32_t internal_hs_cfifo_read(unsigned size)
355{
356 const uint8_t p = internal_hs_curpipe();
357 uint8_t* data = (p == 0U) ? s_hs.din : s_hs.pin[p];
358 uint16_t* rd = (p == 0U) ? &s_hs.din_rd : &s_hs.pin_rd[p];
359 const uint16_t len = (p == 0U) ? s_hs.din_len : s_hs.pin_len[p];
360 uint32_t v = 0U;
361 for (unsigned i = 0U; (i < size) && (*rd < len); i++) {
362 v |= (uint32_t)((uint32_t)data[*rd] << (i * (unsigned)k_usbhs_byte_bits));
363 (*rd)++;
364 }
365 return v;
366}
367
368/* =============================================================================
369 * Bulk pipe helpers.
370 * =============================================================================
371 */
372
382{
383 const uint8_t p = internal_hs_curpipe();
384 if (p == 0U) {
385 /* DCP OUT: a control-write data stage (e.g. a DFU_DNLOAD block). Deliver it
386 * to the device and gate BEMP on the device draining it. A zero-length DCP
387 * BVAL is instead the control-read OUT status ZLP, driven from the CCPL
388 * path -- ignore it here. */
389 if (s_hs.pout_len[0] > 0U) {
390 board_usb_bridge_dcp_out(uc, s_hs.pout[0], s_hs.pout_len[0]);
391 s_hs.pout_wait[0] = true;
392 s_hs.pout_len[0] = 0U;
393 }
394 return;
395 }
396 board_usb_bridge_bulk_out(uc, s_hs.pipe_ep[p], s_hs.pout[p], s_hs.pout_len[p]);
397 s_hs.bulk_out++;
398 /* DEFER the buffer-empty (BEMP) signal until the device firmware drains this
399 * packet from its CFIFO: raising it now lets the polled host push the next
400 * OUT packet immediately, overwriting the undrained bank (which wedged the
401 * multi-packet WRITE(10) data stage -- the device saw only the last chunk and
402 * never produced the CSW). internal_hs_bempsts_read() raises BEMP on device drain. */
403 s_hs.pout_wait[p] = true;
404 s_hs.pout_len[p] = 0U;
405}
406
417{
418 if (s_hs.pout_wait[0] && board_usb_bridge_dcp_out_consumed()) {
419 s_hs.pout_wait[0] = false; /* DCP control-write data stage drained by device. */
421 }
422 for (uint8_t p = 1U; p < (uint8_t)k_usbhs_pipes; p++) {
423 if (s_hs.pout_wait[p] && board_usb_bridge_bulk_out_consumed(s_hs.pipe_ep[p])) {
424 s_hs.pout_wait[p] = false;
425 internal_hs_or((uint16_t)k_ra8_usb_off_bempsts, (uint16_t)(1U << p));
426 }
427 }
428 return internal_hs_reg((uint16_t)k_ra8_usb_off_bempsts);
429}
430
447{
448 s_hs.din_len = 0U;
449 s_hs.din_rd = 0U;
450 s_hs.din_ready = true;
452 if (s_hs.ctrl_breq == (uint8_t)k_usbhs_breq_set_config) {
454 }
455}
456
476RA8_INTERNAL static uint16_t internal_hs_brdysts_read(uc_engine* uc)
477{
478 if (s_hs.ctrl_status_pending && board_usb_bridge_dev_took_ccpl()) {
479 s_hs.ctrl_status_pending = false;
481 }
482 if (!s_hs.din_ready && board_usb_bridge_dcp_in_ready()) {
483 s_hs.din_len = board_usb_bridge_dcp_in_take(s_hs.din, (uint16_t)k_usbhs_din_cap);
484 s_hs.din_rd = 0U;
485 s_hs.din_ready = true;
487 }
488 for (uint8_t p = 1U; p < (uint8_t)k_usbhs_pipes; p++) {
489 const bool armed = (s_hs.pipe_pid[p] & (uint16_t)k_ra8_pid_mask) == (uint16_t)k_ra8_pid_buf;
490 if (!armed || s_hs.pin_ready[p]) {
491 continue;
492 }
493 if (board_usb_bridge_bulk_in_ready(s_hs.pipe_ep[p])) {
494 s_hs.pin_len[p] =
495 board_usb_bridge_bulk_in_take(uc, s_hs.pipe_ep[p], s_hs.pin[p], (uint16_t)k_usbhs_pkt_cap);
496 s_hs.pin_rd[p] = 0U;
497 s_hs.pin_ready[p] = true;
498 s_hs.bulk_in++;
499 internal_hs_or((uint16_t)k_ra8_usb_off_brdysts, (uint16_t)(1U << p));
500 }
501 }
502 return internal_hs_reg((uint16_t)k_ra8_usb_off_brdysts);
503}
504
513RA8_INTERNAL static void internal_hs_brdysts_write(uint16_t value)
514{
515 const uint16_t before = internal_hs_reg((uint16_t)k_ra8_usb_off_brdysts);
516 const uint16_t cleared = (uint16_t)(before & (uint16_t)~value); /* 1 -> 0 transitions. */
517 internal_hs_set((uint16_t)k_ra8_usb_off_brdysts, (uint16_t)(before & value));
518 if ((cleared & (uint16_t)k_usbhs_dcp_bit) != 0U) {
519 s_hs.din_ready = false; /* host consumed the DCP IN packet; allow a re-latch. */
520 }
521 for (uint8_t p = 1U; p < (uint8_t)k_usbhs_pipes; p++) {
522 if ((cleared & (uint16_t)(1U << p)) != 0U) {
523 s_hs.pin_ready[p] = false; /* host acked this pipe's BRDY; allow the next. */
524 }
525 }
526}
527
528/* =============================================================================
529 * Control-transfer bridge -- SETUP launch + status completion.
530 * =============================================================================
531 */
532
541{
542 const uint16_t req = internal_hs_reg((uint16_t)k_ra8_usb_off_usbreq);
543 const uint16_t val = internal_hs_reg((uint16_t)k_ra8_usb_off_usbval);
544 const uint16_t indx = internal_hs_reg((uint16_t)k_ra8_usb_off_usbindx);
545 const uint16_t leng = internal_hs_reg((uint16_t)k_ra8_usb_off_usbleng);
546 s_hs.setup[k_usbhs_setup_bmrt] = (uint8_t)(req & (uint16_t)k_usbhs_byte_mask);
547 s_hs.setup[k_usbhs_setup_breq] =
548 (uint8_t)((req >> (uint16_t)k_usbhs_byte_bits) & (uint16_t)k_usbhs_byte_mask);
549 s_hs.setup[k_usbhs_setup_wval_lo] = (uint8_t)(val & (uint16_t)k_usbhs_byte_mask);
551 (uint8_t)((val >> (uint16_t)k_usbhs_byte_bits) & (uint16_t)k_usbhs_byte_mask);
552 s_hs.setup[k_usbhs_setup_widx_lo] = (uint8_t)(indx & (uint16_t)k_usbhs_byte_mask);
554 (uint8_t)((indx >> (uint16_t)k_usbhs_byte_bits) & (uint16_t)k_usbhs_byte_mask);
555 s_hs.setup[k_usbhs_setup_wlen_lo] = (uint8_t)(leng & (uint16_t)k_usbhs_byte_mask);
557 (uint8_t)((leng >> (uint16_t)k_usbhs_byte_bits) & (uint16_t)k_usbhs_byte_mask);
558}
559
568RA8_INTERNAL static void internal_hs_setup_launch(uc_engine* uc)
569{
571 const uint16_t leng = internal_hs_reg((uint16_t)k_ra8_usb_off_usbleng);
572 s_hs.ctrl_breq = s_hs.setup[k_usbhs_setup_breq];
573 s_hs.ctrl_read =
574 ((s_hs.setup[k_usbhs_setup_bmrt] & (uint8_t)k_usbhs_setup_dir_in) != 0U) && (leng != 0U);
575 s_hs.ctrl_status_pending = false; /* fresh transfer: no deferred status yet. */
576 /* Discard any device CCPL left over from the previous control transfer's
577 * status stage (a control read leaves it unconsumed). Otherwise a deferred
578 * no-data status stage would complete on that stale pulse instead of waiting
579 * for THIS request's completion, re-introducing the host-races-device bug. */
581
582 s_hs.din_ready = false; /* stale DCP IN state cleared for the fresh transfer. */
583 s_hs.din_len = 0U;
584 s_hs.din_rd = 0U;
586 s_hs.setups++;
587
588 /* The SIE latches SACK when the device ACKs the SETUP; the host gates on it. */
589 internal_hs_or((uint16_t)k_ra8_usb_off_intsts1, (uint16_t)(1U << k_ra8_int1_bit_sack));
590}
591
600RA8_INTERNAL static void internal_hs_ccpl_status(uc_engine* uc)
601{
602 if (s_hs.ctrl_read) {
603 /* Control-read OUT status: let the device close its side, then report the
604 * buffer empty so the host's best-effort BEMP wait returns promptly. */
607 return;
608 }
609 /* A host-to-device control write the DEVICE must apply -- every one except
610 * SET_ADDRESS, whose USBADDR latch and IN-ZLP status stage the SIE owns:
611 * DEFER the host's status-stage BRDY until the device firmware has actually
612 * processed the request and pulsed DCPCTR.CCPL. Completing it synchronously
613 * races the polled host past a request the device is still applying:
614 * - no-data (SET_CONFIGURATION): it would issue GET_MAX_LUN and the first
615 * BOT CBW before the MSC class had activated and armed its bulk endpoints;
616 * - with-data (DFU_DNLOAD): it would issue the next SETUP -- clearing the
617 * device DCP-OUT staging -- before the device had drained the block, so
618 * the firmware image never lands. The device pulses CCPL only after it
619 * drains the data stage and applies the request, which is exactly the
620 * completion to wait on. internal_hs_brdysts_read() finishes the stage on it. */
621 if (s_hs.ctrl_breq != (uint8_t)k_usbhs_breq_set_addr) {
622 s_hs.ctrl_status_pending = true;
623 return;
624 }
625 /* SET_ADDRESS: SIE-owned, no device CCPL to wait on -- complete now. */
627}
628
638RA8_INTERNAL static void internal_hs_dcpctr_write(uc_engine* uc, uint16_t value)
639{
640 const uint16_t before = internal_hs_reg((uint16_t)k_ra8_usb_off_dcpctr);
641 const uint16_t sureq = (uint16_t)(1U << k_ra8_dcpctr_bit_sureq);
642 const uint16_t ccpl = (uint16_t)(1U << k_ra8_dcpctr_bit_ccpl);
643 if ((value & sureq) != 0U) {
645 }
646 if (((value & ccpl) != 0U) && ((before & ccpl) == 0U)) {
648 }
649 /* SUREQ self-clears once the SIE has delivered the token; never latch it. */
650 internal_hs_set((uint16_t)k_ra8_usb_off_dcpctr, (uint16_t)(value & (uint16_t)~sureq));
651}
652
653/* =============================================================================
654 * CFIFOCTR write (BCLR / BVAL) + bus-reset edge.
655 * =============================================================================
656 */
657
667RA8_INTERNAL static void internal_hs_cfifoctr_write(uc_engine* uc, uint16_t value)
668{
669 const uint8_t p = internal_hs_curpipe();
670 if ((value & (uint16_t)k_ra8_fifoctr_bclr) != 0U) {
671 if (p == 0U) {
672 s_hs.din_len = 0U;
673 s_hs.din_rd = 0U;
674 s_hs.din_ready = false;
675 } else {
676 s_hs.pout_len[p] = 0U;
677 s_hs.pin_rd[p] = s_hs.pin_len[p];
678 }
679 }
680 if ((value & (uint16_t)k_ra8_fifoctr_bval) != 0U) {
682 }
683}
684
694RA8_INTERNAL static void internal_hs_dvstctr0_write(uc_engine* uc, uint16_t value)
695{
696 const bool usbrst = (value & (uint16_t)(1U << k_usbhs_dvstctr_usbrst)) != 0U;
697 internal_hs_set((uint16_t)k_ra8_usb_off_dvstctr0, value);
698 if (s_hs.prev_usbrst && !usbrst) {
699 board_usb_bridge_bus_reset(uc); /* USBRST released: device returns to Default. */
700 }
701 s_hs.prev_usbrst = usbrst;
702}
703
704/* =============================================================================
705 * MMIO read / write dispatch.
706 * =============================================================================
707 */
708
721RA8_INTERNAL static uint64_t internal_hs_read(uc_engine* uc, uint64_t off, unsigned size)
722{
723 switch ((uint16_t)off) {
724 case (uint16_t)k_ra8_usbhs_off_pllsta:
725 return (uint64_t)k_usbhs_plllock; /* PHY PLL reported locked so init proceeds. */
726 case (uint16_t)k_ra8_usb_off_syssts0:
727 return board_usb_dev_attached() ? (uint64_t)k_usbhs_lnst_j : 0U;
728 case (uint16_t)k_ra8_usb_off_dvstctr0: {
729 uint16_t v = internal_hs_reg((uint16_t)k_ra8_usb_off_dvstctr0);
731 v = (uint16_t)(v | (uint16_t)k_usbhs_rhst_fs); /* connected FS device speed. */
732 }
733 return (uint64_t)v;
734 }
735 case (uint16_t)k_ra8_usb_off_cfifo:
736 return (uint64_t)internal_hs_cfifo_read(size);
737 case (uint16_t)k_ra8_usb_off_cfifoctr:
738 return (uint64_t)((uint16_t)k_ra8_fifoctr_frdy |
740 case (uint16_t)k_ra8_usb_off_brdysts:
741 return (uint64_t)internal_hs_brdysts_read(uc);
742 case (uint16_t)k_ra8_usb_off_bempsts:
743 return (uint64_t)internal_hs_bempsts_read();
744 case (uint16_t)k_ra8_usb_off_pipemaxp:
745 return (uint64_t)s_hs.pipe_maxp[internal_hs_pipesel()];
746 default:
747 break;
748 }
749 if (internal_hs_is_pipectr(off)) {
750 return (uint64_t)s_hs.pipe_pid[internal_hs_pipectr_index(off)];
751 }
752 return (uint64_t)internal_hs_reg((uint16_t)off);
753}
754
766RA8_INTERNAL static void
767internal_hs_write(uc_engine* uc, uint64_t off, unsigned size, uint64_t value)
768{
769 const uint16_t v16 = (uint16_t)value;
770 switch ((uint16_t)off) {
771 case (uint16_t)k_ra8_usb_off_cfifo:
772 /* CFIFOH tail. */
773 case (uint16_t)((uint16_t)k_ra8_usb_off_cfifo + (uint16_t)k_usbhs_cfifo_h):
774 /* CFIFOHH tail. */
775 case (uint16_t)((uint16_t)k_ra8_usb_off_cfifo + (uint16_t)k_usbhs_cfifo_hh):
776 internal_hs_cfifo_write((uint32_t)value, size); /* full width: HS CFIFO is 32-bit (MBW=32). */
777 return;
778 case (uint16_t)k_ra8_usb_off_cfifoctr:
780 return;
781 case (uint16_t)k_ra8_usb_off_dcpctr:
783 return;
784 case (uint16_t)k_ra8_usb_off_dvstctr0:
786 return;
787 case (uint16_t)k_ra8_usb_off_brdysts:
789 return;
790 case (uint16_t)k_ra8_usb_off_bempsts:
791 case (uint16_t)k_ra8_usb_off_nrdysts:
792 case (uint16_t)k_ra8_usb_off_intsts0:
793 case (uint16_t)k_ra8_usb_off_intsts1:
794 internal_hs_set((uint16_t)off, (uint16_t)(internal_hs_reg((uint16_t)off) & v16)); /* W0C. */
795 return;
796 case (uint16_t)k_ra8_usb_off_pipecfg:
797 s_hs.pipe_ep[internal_hs_pipesel()] = (uint8_t)(v16 & (uint16_t)k_usbhs_epnum_mask);
798 internal_hs_set((uint16_t)off, v16);
799 return;
800 case (uint16_t)k_ra8_usb_off_pipemaxp:
801 s_hs.pipe_maxp[internal_hs_pipesel()] = v16;
802 return;
803 default:
804 break;
805 }
806 if (internal_hs_is_pipectr(off)) {
807 s_hs.pipe_pid[internal_hs_pipectr_index(off)] = v16;
808 return;
809 }
810 internal_hs_set((uint16_t)off, v16);
811}
812
813/* =============================================================================
814 * Role-routed public entry points (see the role-routing section of board_usb.h).
815 * =============================================================================
816 */
817
818uint64_t board_usbhs_host_reg_read(uc_engine* uc, uint64_t off, unsigned size)
819{
820 return internal_hs_read(uc, off, size);
821}
822
823void board_usbhs_host_reg_write(uc_engine* uc, uint64_t off, unsigned size, uint64_t value)
824{
825 internal_hs_write(uc, off, size, value);
826}
827
828uint32_t board_usbhs_host_shadow_handoff(uint16_t* dst_words, uint32_t word_capacity)
829{
830 if (dst_words == nullptr) {
831 return 0U;
832 }
833 uint32_t n = (uint32_t)k_usbhs_words;
834 if (n > word_capacity) {
835 n = word_capacity;
836 }
837 (void)memcpy(dst_words, s_hs.reg, (size_t)n * sizeof(uint16_t));
838 /* The embedded-PHY page past the device-model span (LPSTS.SUSPENDM at 0x102)
839 * is per-WINDOW state, not per-role: it stays with this physical controller
840 * so the device firmware's PHY configuration survives the rebinding. */
841 uint16_t phy_page[k_usbhs_words] = {};
842 (void)memcpy(&phy_page[(uint32_t)k_usbhs_dev_span / 2U],
843 &s_hs.reg[(uint32_t)k_usbhs_dev_span / 2U],
844 ((size_t)k_usbhs_words - ((size_t)k_usbhs_dev_span / 2U)) * sizeof(uint16_t));
845 s_hs = (usbhs_state_t){}; /* fresh start behind the USBFS window (Config B). */
846 (void)memcpy(s_hs.reg, phy_page, sizeof(phy_page));
847 return n;
848}
849
850/* =============================================================================
851 * Block entry points, reset, report, self-registration.
852 * =============================================================================
853 */
854
874RA8_INTERNAL static uint64_t internal_usbhs_block_read(uc_engine* uc, uint64_t addr, unsigned size)
875{
876 const uint64_t off = addr - (uint64_t)k_usbhs_base;
877 if ((uint16_t)off == (uint16_t)k_ra8_usbhs_off_pllsta) {
878 return (uint64_t)
879 k_usbhs_plllock; /* HUM Ch 37.2.4 "PLLSTA : PLL Status Register" p 2065 -- role-agnostic. */
880 }
882 if (off >= (uint64_t)k_usbhs_dev_span) {
883 return (uint64_t)internal_hs_reg((uint16_t)off); /* PHY page (LPSTS): local shadow. */
884 }
885 return board_usb_dev_reg_read(uc, off, size);
886 }
887 return internal_hs_read(uc, off, size);
888}
889
907RA8_INTERNAL static void
908internal_usbhs_block_write(uc_engine* uc, uint64_t addr, unsigned size, uint64_t value)
909{
910 const uint64_t off = addr - (uint64_t)k_usbhs_base;
911 /* Role declaration (HUM Ch 37.2.1 SYSCFG.DPRPU, p 2060): only the device
912 * role pulls D+ up. A DPRPU=1 write to THIS instance pins it to the device
913 * model -- the FS controller must then be the loop's host (Config B). */
914 if (!board_usb_roles_swapped() && ((uint16_t)off == (uint16_t)k_ra8_usb_off_syscfg) &&
915 (((uint16_t)value & (uint16_t)(1U << k_ra8_syscfg_bit_dprpu)) != 0U)) {
917 }
919 if (off >= (uint64_t)k_usbhs_dev_span) {
920 internal_hs_set((uint16_t)off, (uint16_t)value); /* PHY page (LPSTS): local shadow. */
921 return;
922 }
923 board_usb_dev_reg_write(uc, off, size, value);
924 return;
925 }
926 internal_hs_write(uc, off, size, value);
927}
928
937{
938 s_hs = (usbhs_state_t){};
939}
940
949{
950 if (s_hs.setups == 0U) {
951 return; /* the loop's host side never came up this run; stay silent. */
952 }
953 (void)priv_emu_io_errf(
954 " %s host : %u SETUP(s), %u bulk-OUT, %u bulk-IN (chip-internal loop)\n",
955 board_usb_roles_swapped() ? "USB-FS" : "USB-HS",
956 s_hs.setups,
957 s_hs.bulk_out,
958 s_hs.bulk_in);
959}
960
963 .base = (uint64_t)k_usbhs_base,
964 .span = (uint64_t)k_usbhs_span,
965 .order = (uint32_t)k_usbhs_report_ord,
968 .tick = nullptr,
969 .reset = internal_usbhs_reset,
970 .report = internal_usbhs_report,
971 .name = "USBHS-host",
972 .observe = false,
973 .device = k_board_block_dev_any,
974 .loop_only = true,
975};
976
978[[gnu::constructor]] RA8_INTERNAL static void internal_usbhs_block_register(void)
979{
981}
Decentralized peripheral-block registry for the board emulator core.
void board_periph_register_block(const board_periph_block_t *block)
Register a peripheral block's descriptor with the core registry.
@ k_board_block_dev_any
Present on every modelled device (default).
static void internal_hs_set(uint16_t off, uint16_t value)
Write a 16-bit shadow register at window byte offset off.
static void internal_usbhs_reset(void)
Reset the USBHS host model to power-on state.
static uint64_t internal_hs_read(uc_engine *uc, uint64_t off, unsigned size)
Read one USBHS host register at window byte offset off.
usbhs_lines_t
SYSSTS0 / DVSTCTR0 host-role line-state fields (HUM Ch 37.2.3 / 37.2.5).
@ k_usbhs_lnst_j
SYSSTS0.LNST J-state: an FS device is attached.
@ k_usbhs_rhst_fs
DVSTCTR0.RHST = full speed.
static void internal_usbhs_block_write(uc_engine *uc, uint64_t addr, unsigned size, uint64_t value)
MMIO write handler: dispatch inside the USBHS controller window.
void board_usbhs_host_reg_write(uc_engine *uc, uint64_t off, unsigned size, uint64_t value)
Host-model register write by window byte offset (role routing).
static void internal_hs_dcpctr_write(uc_engine *uc, uint16_t value)
Apply a DCPCTR write: SUREQ launches a SETUP, CCPL drives the status.
uint64_t board_usbhs_host_reg_read(uc_engine *uc, uint64_t off, unsigned size)
Host-model register read by window byte offset (role routing).
static uint16_t internal_hs_brdysts_read(uc_engine *uc)
Latch any device IN data the host is now polling for (BRDY sources).
uint32_t board_usbhs_host_shadow_handoff(uint16_t *dst_words, uint32_t word_capacity)
Hand the host model's register shadow off and reset the host model.
static void internal_hs_cfifoctr_write(uc_engine *uc, uint16_t value)
Apply a CFIFOCTR write: BCLR flushes the buffer, BVAL commits a bulk OUT.
usbhs_setup_idx_t
SETUP-packet byte offsets (USB 2.0 sec 9.3, little-endian fields).
@ k_usbhs_setup_wval_lo
wValue low byte.
@ k_usbhs_setup_widx_hi
wIndex high byte.
@ k_usbhs_setup_wlen_hi
wLength high byte.
@ k_usbhs_setup_breq
bRequest.
@ k_usbhs_setup_bmrt
bmRequestType.
@ k_usbhs_setup_wlen_lo
wLength low byte.
@ k_usbhs_setup_widx_lo
wIndex low byte.
@ k_usbhs_setup_wval_hi
wValue high byte.
usbhs_dvstctr_bit_t
DVSTCTR0 host-control bit positions (HUM Ch 37.2.5, p 2065).
@ k_usbhs_dvstctr_usbrst
USBRST: drive bus reset.
static void internal_usbhs_block_register(void)
Register the USBHS host block before main (host constructor).
usbhs_lit_t
Small shared literals (avoid bare numeric literals).
@ k_usbhs_cfifo_h
CFIFOH alias offset from CFIFO (+0x2).
@ k_usbhs_byte_mask
One-byte mask.
@ k_usbhs_cfifo_hh
CFIFOHH alias offset from CFIFO (+0x3).
@ k_usbhs_epnum_mask
PIPECFG endpoint-number field.
@ k_usbhs_byte_bits
Bits per byte.
static void internal_hs_bulk_out_commit(uc_engine *uc)
Deliver the selected bulk pipe's accumulated OUT payload to the device.
static uint32_t internal_hs_cfifo_read(unsigned size)
Read size bytes (LSB-first) from the selected IN staging buffer.
static uint16_t internal_hs_cfifo_dtln(void)
Bytes still available for the host to read on the selected IN staging.
static bool internal_hs_is_pipectr(uint64_t off)
True when off is inside the PIPECTR[9] array window.
static void internal_hs_setup_launch(uc_engine *uc)
Launch a SETUP (DCPCTR.SUREQ write): deliver it and latch SACK.
static const board_periph_block_t s_k_usbhs_block
USBHS host block descriptor (loop-only: owns 0x40351000 with –usbhs-loop).
static uint64_t internal_usbhs_block_read(uc_engine *uc, uint64_t addr, unsigned size)
MMIO read handler: dispatch inside the USBHS controller window.
static uint16_t internal_hs_bempsts_read(void)
BEMPSTS read: raise a pipe's BEMP once the device has drained its OUT packet.
usbhs_usb_std_t
Standard USB chapter-9 SETUP field values the model classifies on.
@ k_usbhs_setup_dir_in
bmRequestType device-to-host bit (USB 2.0 9.3).
@ k_usbhs_breq_set_config
SET_CONFIGURATION bRequest (USB 2.0 9.4).
@ k_usbhs_breq_set_addr
SET_ADDRESS bRequest (USB 2.0 9.4).
usbhs_pllsta_bit_t
PLLSTA lock flag reported so the firmware's PHY PLL wait completes.
@ k_usbhs_plllock
PLLSTA.PLLLOCK (HUM Ch 37.2.4 "PLLSTA" p 2064).
static void internal_hs_ctrl_status_complete(uc_engine *uc)
Finish a no-data / control-write IN status stage.
static usbhs_state_t s_hs
static void internal_usbhs_report(void)
End-of-run summary: enumeration + bulk traffic bridged to the device.
usbhs_status_bit_t
DCP (pipe 0) status bit shared by BRDYSTS / BEMPSTS / NRDYSTS.
@ k_usbhs_dcp_bit
BRDYSTS/BEMPSTS/NRDYSTS DCP (pipe 0) bit.
static uint16_t internal_hs_reg(uint16_t off)
Read a 16-bit shadow register at window byte offset off.
static uint8_t internal_hs_curpipe(void)
Currently-selected CFIFO pipe (CFIFOSEL.CURPIPE[3:0], modulo pipe count).
static uint8_t internal_hs_pipectr_index(uint64_t off)
PIPECTR array index (PIPE1..PIPE9, modulo pipe count) for off.
static void internal_hs_brdysts_write(uint16_t value)
Apply a BRDYSTS W0C write: a written 0 clears that bit, a 1 preserves it.
static void internal_hs_or(uint16_t off, uint16_t bits)
OR bits into the 16-bit shadow register at off.
static void internal_hs_write(uc_engine *uc, uint64_t off, unsigned size, uint64_t value)
Write one USBHS host register at window byte offset off.
static void internal_hs_ccpl_status(uc_engine *uc)
Handle a DCPCTR.CCPL rising edge: drive the control-transfer status stage.
static void internal_hs_capture_setup(void)
Copy USBREQ..USBLENG from the shadow into the 8-byte SETUP buffer.
static uint8_t internal_hs_pipesel(void)
The host pipe currently selected by PIPESEL (modulo pipe count).
usbhs_geom_t
USBHS host register-window geometry and staging-buffer caps.
@ k_usbhs_report_ord
End-of-run report slot (after USB-FS).
@ k_usbhs_pkt_cap
Per-pipe staging cap (HS bulk MPS).
@ k_usbhs_base
USBHS base (HUM Ch 37, p 2059).
@ k_usbhs_setup_len
SETUP packet length.
@ k_usbhs_pipes
DCP (0) + PIPE1..PIPE9.
@ k_usbhs_din_cap
DCP IN staging cap (full config desc).
@ k_usbhs_words
16-bit register-shadow word count.
@ k_usbhs_span
Modelled window (covers DEVADD / LPSTS).
@ k_usbhs_dev_span
Device-model span; PHY page lies past it.
static uint32_t internal_hs_word(uint64_t off)
16-bit shadow word index for a window byte offset.
static void internal_hs_cfifo_write(uint32_t value, unsigned size)
Append size bytes of value (LSB-first) to the selected OUT pipe.
static void internal_hs_dvstctr0_write(uc_engine *uc, uint16_t value)
Apply a DVSTCTR0 write, bridging a host bus-reset edge onto the device.
USBFS controller model + a virtual USB host for the board emulator.
void board_usb_bridge_mark_configured(uc_engine *uc)
Mark the device configured after a host SET_CONFIGURATION.
bool board_usb_bridge_bulk_out_consumed(uint8_t dev_pipe)
Whether the device has drained the last host bulk-OUT packet.
bool board_usb_bridge_dcp_out_consumed(void)
Whether the device has drained the last host DCP control-OUT packet.
void board_usb_bridge_ctrl_status(uc_engine *uc)
Drive the read-status stage of a host control-read (raise CTRT).
bool board_usb_roles_swapped(void)
Whether the self-loop window roles are swapped (Config B).
bool board_usb_bridge_dcp_in_ready(void)
Whether the device has queued a control-IN response on the DCP.
void board_usb_bridge_deliver_setup(uc_engine *uc, const uint8_t *setup)
Deliver an 8-byte host SETUP packet to the device (raise its CTRT).
uint16_t board_usb_bridge_bulk_in_take(uc_engine *uc, uint8_t dev_pipe, uint8_t *buf, uint16_t cap)
Consume a device bulk-IN packet from dev_pipe into buf.
void board_usb_dev_reg_write(uc_engine *uc, uint64_t off, unsigned size, uint64_t value)
Device-model register write by window byte offset (role routing).
void board_usb_roles_swap(uc_engine *uc)
Swap the self-loop window<->model bindings (enter Config B).
uint64_t board_usb_dev_reg_read(uc_engine *uc, uint64_t off, unsigned size)
Device-model register read by window byte offset (role routing).
void board_usb_bridge_bus_reset(uc_engine *uc)
Bridge a host bus reset onto the device (advance DVSQ to Default).
bool board_usb_dev_attached(void)
Whether the modelled USBFS device has asserted its D+ pull-up (DPRPU).
bool board_usb_bridge_bulk_in_ready(uint8_t dev_pipe)
Whether the device has queued a bulk-IN packet on dev_pipe.
void board_usb_bridge_dcp_out(uc_engine *uc, const uint8_t *data, uint16_t len)
Deliver a host control-write data-stage packet to the device DCP.
uint16_t board_usb_bridge_dcp_in_take(uint8_t *buf, uint16_t cap)
Consume the device's queued control-IN response into buf.
void board_usb_bridge_bulk_out(uc_engine *uc, uint8_t dev_pipe, const uint8_t *data, uint16_t len)
Deliver a host bulk-OUT packet to a device endpoint pipe.
bool board_usb_bridge_dev_took_ccpl(void)
Take (consume) the device's control-transfer completion (DCPCTR.CCPL).
Bounded raw-descriptor I/O seam for the RA8 emulator.
emu_io_result_t priv_emu_io_errf(const char *format,...)
Format bounded text and write it to the injected error descriptor.
-proof
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
void * memcpy(void *dst, const void *src, size_t n)
Copy memory area between non-overlapping regions.
USB Full-Speed + High-Speed controller layout for the Renesas RA8D2.
@ k_ra8_int1_bit_sack
SETUP transaction ACKed.
@ k_ra8_pid_buf
BUF response (transmit/receive).
@ k_ra8_pid_mask
PID[1:0] field mask.
@ k_ra8_syscfg_bit_dprpu
D+ pull-up enable (device).
@ k_ra8_usb_off_pipecfg
PIPECFG : Selected pipe cfg.
@ k_ra8_usb_off_pipectr
PIPECTR : PIPE1..9 control.
@ k_ra8_usb_off_syssts0
SYSSTS0 : System status 0.
@ k_ra8_usb_off_usbleng
USBLENG : USB request wLength.
@ k_ra8_usb_off_dvstctr0
DVSTCTR0 : Device state ctrl.
@ k_ra8_usb_off_usbindx
USBINDX : USB request wIndex.
@ k_ra8_usb_off_nrdysts
NRDYSTS : NRDY Status.
@ k_ra8_usb_off_usbval
USBVAL : USB request wValue.
@ k_ra8_usb_off_cfifosel
CFIFOSEL : Control FIFO select.
@ k_ra8_usb_off_bempsts
BEMPSTS : BEMP Status.
@ k_ra8_usb_off_intsts1
INTSTS1 : Interrupt Status 1.
@ k_ra8_usb_off_cfifo
CFIFO : Control FIFO port.
@ k_ra8_usb_off_dcpctr
DCPCTR : DCP control.
@ k_ra8_usb_off_intsts0
INTSTS0 : Interrupt Status 0.
@ k_ra8_usb_off_usbreq
USBREQ : USB request type.
@ k_ra8_usb_off_pipemaxp
PIPEMAXP : Selected pipe maxp.
@ k_ra8_usb_off_brdysts
BRDYSTS : BRDY Status.
@ k_ra8_usb_off_pipesel
PIPESEL : Pipe window select.
@ k_ra8_usb_off_cfifoctr
CFIFOCTR : Control FIFO control.
@ k_ra8_usb_off_syscfg
SYSCFG : System config.
@ k_ra8_fifoctr_frdy
FIFO ready.
@ k_ra8_fifoctr_bclr
Buffer clear.
@ k_ra8_fifoctr_dtln
Data length (write-bytes-rem).
@ k_ra8_fifoctr_bval
Buffer valid.
@ k_ra8_dcpctr_bit_sureq
Send USB request (host mode).
@ k_ra8_dcpctr_bit_ccpl
Control transfer end enable.
@ k_ra8_fifosel_curpipe
Current pipe select [3:0].
@ k_ra8_usb_pipectr_count
PIPECTR[9] = PIPE1..PIPE9.
@ k_ra8_usbhs_off_pllsta
PLLSTA : PLL lock flag.
A modelled peripheral block's self-description for the core registry.
Aggregate USBHS host-controller model state.
uint16_t reg[k_usbhs_words]
16-bit reflect-on-read register shadow.
uint32_t bulk_out
Bulk-OUT packets delivered.
uint16_t din_len
Bytes staged in din.
uint8_t din[k_usbhs_din_cap]
DCP IN staging (device -> host control data).
bool pin_ready[k_usbhs_pipes]
Pipe holds an unread packet.
uint8_t pout[k_usbhs_pipes][k_usbhs_pkt_cap]
Per-pipe OUT staging (host->dev).
bool prev_usbrst
Previous DVSTCTR0.USBRST level (reset-edge detect).
bool pout_wait[k_usbhs_pipes]
OUT pipe awaiting device drain (BEMP).
uint8_t ctrl_breq
Last SETUP bRequest.
uint16_t pin_rd[k_usbhs_pipes]
Read cursor per IN pipe.
uint8_t pin[k_usbhs_pipes][k_usbhs_pkt_cap]
Per-pipe IN staging (dev->host).
bool ctrl_read
Last SETUP was a control read.
bool ctrl_status_pending
Control-write status awaits dev CCPL.
uint16_t pipe_pid[k_usbhs_pipes]
PIPECTR PID shadow per host pipe.
uint8_t setup[k_usbhs_setup_len]
Latched 8-byte SETUP packet.
uint16_t din_rd
Read cursor as the host drains din.
bool din_ready
din holds an unread packet (DCP BRDY source).
uint16_t pin_len[k_usbhs_pipes]
Bytes staged per IN pipe.
uint32_t bulk_in
Bulk-IN packets collected.
uint8_t pipe_ep[k_usbhs_pipes]
Device endpoint number per host pipe.
uint32_t setups
SETUP packets bridged to the device.
uint16_t pout_len[k_usbhs_pipes]
Bytes accumulated per OUT pipe.
uint16_t pipe_maxp[k_usbhs_pipes]
PIPEMAXP shadow per host pipe.