From 579bfc2b8027934fedd5725c4df7d836017736e3 Mon Sep 17 00:00:00 2001 From: Lucas Bollen Date: Tue, 20 Jan 2026 17:15:40 +0100 Subject: [PATCH 1/6] Use `addressableBytesWb` in `wbStorage` From 36d9f3bbd3e05ff52a765a370cc8eb8417c93c26 Mon Sep 17 00:00:00 2001 From: Lucas Bollen Date: Tue, 20 Jan 2026 17:15:40 +0100 Subject: [PATCH 2/6] Use `addressableBytesWb` in `wbStorage` From 4c45ee9d8ecd0bbeb90330bea7ed1624a1ba5b25 Mon Sep 17 00:00:00 2001 From: Lucas Bollen Date: Tue, 20 Jan 2026 17:15:40 +0100 Subject: [PATCH 3/6] Use `addressableBytesWb` in `wbStorage` From 903637cb541991cc087d59a1793b92e19143f83b Mon Sep 17 00:00:00 2001 From: Lucas Bollen Date: Tue, 20 Jan 2026 17:15:40 +0100 Subject: [PATCH 4/6] Use `addressableBytesWb` in `wbStorage` From f401cc8a50756745291eea6296125af31b2db080 Mon Sep 17 00:00:00 2001 From: Lucas Bollen Date: Tue, 20 Jan 2026 17:15:40 +0100 Subject: [PATCH 5/6] Use `addressableBytesWb` in `wbStorage` From 78adde2ecc42529c1a7ba27ba085c617ac893673 Mon Sep 17 00:00:00 2001 From: Lucas Bollen Date: Mon, 2 Mar 2026 13:49:01 +0100 Subject: [PATCH 6/6] Replace scatter unit and gather unit in `softUgnDemo` with ringbuffers --- .../Instances/Hitl/SoftUgnDemo/Core.hs | 41 +++--- .../demos/soft-ugn-mu/src/main.c | 32 ++--- .../demos/soft-ugn-mu/src/main.rs | 43 ------ .../demos/soft-ugn-mu/src/ringbuffer_align.c | 54 ++++---- .../demos/soft-ugn-mu/src/ringbuffer_align.h | 33 ++--- .../sim-tests/ringbuffer_test/src/main.rs | 4 +- .../bittide-hal-c/include/bittide_gather.h | 5 +- .../include/bittide_ring_receive.h | 127 ++++++++++++++++++ .../include/bittide_ring_transmit.h | 120 +++++++++++++++++ .../bittide-hal-c/include/bittide_scatter.h | 2 - .../bittide-hal-c/include/bittide_ugn.h | 67 +++++---- .../src/manual_additions/calendar.rs | 15 --- .../soft_ugn_demo_mu/scatter_gather.rs | 40 ++---- 13 files changed, 371 insertions(+), 212 deletions(-) create mode 100644 firmware-support/bittide-hal-c/include/bittide_ring_receive.h create mode 100644 firmware-support/bittide-hal-c/include/bittide_ring_transmit.h diff --git a/bittide-instances/src/Bittide/Instances/Hitl/SoftUgnDemo/Core.hs b/bittide-instances/src/Bittide/Instances/Hitl/SoftUgnDemo/Core.hs index 30cf758f9..18ef9f888 100644 --- a/bittide-instances/src/Bittide/Instances/Hitl/SoftUgnDemo/Core.hs +++ b/bittide-instances/src/Bittide/Instances/Hitl/SoftUgnDemo/Core.hs @@ -7,11 +7,10 @@ import Clash.Explicit.Prelude import Clash.Prelude (HiddenClockResetEnable, withClockResetEnable) import Protocols -import Bittide.Calendar (CalendarConfig (..), ValidEntry (..)) import Bittide.CaptureUgn (captureUgn) import Bittide.ClockControl (SpeedChange) import Bittide.ClockControl.CallistoSw (SwcccInternalBusses, callistoSwClockControlC) -import Bittide.DoubleBufferedRam (wbStorage) +import Bittide.DoubleBufferedRam (blockRamByteAddressableU, wbStorage) import Bittide.ElasticBuffer (xilinxElasticBufferWb) import Bittide.Instances.Domains (Basic125, Bittide, GthRx) import Bittide.Instances.Hitl.Setup (LinkCount) @@ -23,7 +22,7 @@ import Bittide.ProcessingElement ( RemainingBusWidth, processingElement, ) -import Bittide.ScatterGather +import Bittide.Ringbuffer (receiveRingbufferWb, transmitRingbufferWb) import Bittide.SharedTypes (Bitbone, BitboneMm) import Bittide.Sync (Sync) import Bittide.Wishbone (readDnaPortE2WbWorker, timeWb, uartBytes, uartInterfaceWb) @@ -56,12 +55,10 @@ type NmuInternalBusses = 3 + PeInternalBusses {- Busses per link: - UGN component - Elastic buffer - - Scatter unit - - Scatter calendar - - Gather unit - - Gather calendar + - Receive ringbuffer + - Transmit ringbuffer -} -type PeripheralsPerLink = 6 +type PeripheralsPerLink = 4 {- External busses: - Transceivers @@ -176,8 +173,8 @@ core (refClk, refRst) (bitClk, bitRst, bitEna) rxClocks rxResets = withBittideClockResetEnable managementUnit localCounter maybeDna -< (muMm, muJtag) (ugnWbs, muWbs1) <- Vec.split -< muWbAll (ebWbs, muWbs2) <- Vec.split -< muWbs1 - (scatterBusses, scatterCalendarBusses, muWbs3) <- Vec.split3 -< muWbs2 - (gatherBusses, gatherCalendarBusses, muWbs4) <- Vec.split3 -< muWbs3 + (rxBufferBusses, muWbs3) <- Vec.split -< muWbs2 + (txBufferBusses, muWbs4) <- Vec.split -< muWbs3 [muTransceiverBus, muCallistoBus] <- idC -< muWbs4 -- Stop management unit @@ -198,7 +195,7 @@ core (refClk, refRst) (bitClk, bitRst, bitEna) rxClocks rxResets = -- Use of `dflipflop` to add pipelining should be replaced by -- https://github.com/bittide/bittide-hardware/pull/1134 - Fwd rxs2 <- + rxs2 <- withBittideClockResetEnable $ Vec.vecCircuits ((captureUgn localCounter . dflipflop bitClk) <$> rxs1) -< ugnWbs @@ -206,25 +203,17 @@ core (refClk, refRst) (bitClk, bitRst, bitEna) rxClocks rxResets = -- Start ringbuffers let - maxCalDepth = SNat @4000 - scatterConfig = ScatterConfig maxCalDepth (CalendarConfig maxCalDepth repetitionBits sgCal sgCal) - gatherConfig = GatherConfig maxCalDepth (CalendarConfig maxCalDepth repetitionBits sgCal sgCal) - repetitionBits = d16 - sgCal = ValidEntry 0 (snatToNum maxCalDepth - 1) :> Nil - - scatterCalendarBussesDelayed <- - repeatC (fmapC $ withBittideClockResetEnable delayWishbone) -< scatterCalendarBusses - gatherCalendarBussesDelayed <- - repeatC (fmapC $ withBittideClockResetEnable delayWishbone) -< gatherCalendarBusses + bufferDepth = SNat @4000 + rxPrim ena = blockRamU bitClk bitRst ena NoClearOnReset bufferDepth + txPrim ena = withClockResetEnable bitClk bitRst ena blockRamByteAddressableU idleSink - <| Vec.vecCircuits (fmap (withBittideClockResetEnable (scatterUnitWbC scatterConfig)) rxs2) + <| fmapC (withBittideClockResetEnable receiveRingbufferWb rxPrim bufferDepth) <| Vec.zip - -< (scatterBusses, scatterCalendarBussesDelayed) + -< (rxBufferBusses, rxs2) Fwd txs <- - repeatC (withBittideClockResetEnable (gatherUnitWbC gatherConfig)) - <| Vec.zip - -< (gatherBusses, gatherCalendarBussesDelayed) + fmapC (withBittideClockResetEnable transmitRingbufferWb txPrim bufferDepth) -< txBufferBusses + -- Stop ringbuffers -- Start clock control diff --git a/firmware-binaries/demos/soft-ugn-mu/src/main.c b/firmware-binaries/demos/soft-ugn-mu/src/main.c index d7d4179bb..46bc2e89b 100644 --- a/firmware-binaries/demos/soft-ugn-mu/src/main.c +++ b/firmware-binaries/demos/soft-ugn-mu/src/main.c @@ -5,10 +5,10 @@ #include "hals/soft_ugn_demo_mu/device_instances.h" #include "bittide_dna.h" -#include "bittide_gather.h" // Linter says we dont need this include, but we do -#include "bittide_scatter.h" // Linter says we dont need this include, but we do +#include "bittide_ring_receive.h" +#include "bittide_ring_transmit.h" #include "bittide_timer.h" -#include "bittide_ugn.h" // Requires the `bittide_scatter.h` and `bittide_gather.h` +#include "bittide_ugn.h" #include "messages.h" #include "priority_queue.h" @@ -21,7 +21,6 @@ // Application Configuration // ============================================================================ -ScatterUnit su; // Number of event loop iterations #define NUM_PERIODS 1000 #define NUM_PORTS 7 @@ -57,15 +56,17 @@ int c_main(void) { // Initialize all peripherals Uart uart = hal.uart; - ScatterUnit scatter_units[NUM_PORTS] = { - hal.scatter_unit_0, hal.scatter_unit_1, hal.scatter_unit_2, - hal.scatter_unit_3, hal.scatter_unit_4, hal.scatter_unit_5, - hal.scatter_unit_6}; - - GatherUnit gather_units[NUM_PORTS] = {hal.gather_unit_0, hal.gather_unit_1, - hal.gather_unit_2, hal.gather_unit_3, - hal.gather_unit_4, hal.gather_unit_5, - hal.gather_unit_6}; + ReceiveRingbuffer receive_ringbuffers[NUM_PORTS] = { + hal.receive_ringbuffer_0, hal.receive_ringbuffer_1, + hal.receive_ringbuffer_2, hal.receive_ringbuffer_3, + hal.receive_ringbuffer_4, hal.receive_ringbuffer_5, + hal.receive_ringbuffer_6}; + + TransmitRingbuffer transmit_ringbuffers[NUM_PORTS] = { + hal.transmit_ringbuffer_0, hal.transmit_ringbuffer_1, + hal.transmit_ringbuffer_2, hal.transmit_ringbuffer_3, + hal.transmit_ringbuffer_4, hal.transmit_ringbuffer_5, + hal.transmit_ringbuffer_6}; Timer timer = hal.timer; dna_t dna; dna_read(hal.dna, dna); @@ -84,8 +85,9 @@ int c_main(void) { UgnEdge outgoing_link_ugn_list[NUM_PORTS]; UgnContext ugn_ctx; - ugn_context_init(&ugn_ctx, scatter_units, gather_units, NUM_PORTS, node_id, - incoming_link_ugn_list, outgoing_link_ugn_list, NUM_PORTS); + ugn_context_init(&ugn_ctx, receive_ringbuffers, transmit_ringbuffers, + NUM_PORTS, node_id, incoming_link_ugn_list, + outgoing_link_ugn_list, NUM_PORTS); // Print consolidated initialization information PRINT_INIT_INFO(uart, &ugn_ctx, BUFFER_SIZE, SEND_PERIOD, RECEIVE_PERIOD, diff --git a/firmware-binaries/demos/soft-ugn-mu/src/main.rs b/firmware-binaries/demos/soft-ugn-mu/src/main.rs index 61edff2e4..5e7a2e0af 100644 --- a/firmware-binaries/demos/soft-ugn-mu/src/main.rs +++ b/firmware-binaries/demos/soft-ugn-mu/src/main.rs @@ -6,8 +6,6 @@ // SPDX-License-Identifier: Apache-2.0 use bittide_hal::hals::soft_ugn_demo_mu::DeviceInstances; -use bittide_hal::manual_additions::calendar::RingbufferCalendar; -use bittide_hal::shared_devices::Uart; use bittide_sys::link_startup::LinkStartup; use bittide_sys::stability_detector::Stability; use core::panic::PanicInfo; @@ -23,41 +21,6 @@ extern "C" { fn c_main() -> !; } -/// Initialize scatter and gather calendars with incrementing counter entries. -/// Each calendar entry has a duration of 0 (no repeat), with 4000 entries total. -#[no_mangle] -#[inline(never)] -fn initialize_calendars(uart: &mut Uart) { - const NUM_ENTRIES: usize = 4000; - - let calendars = [ - // Scatter calendars - &INSTANCES.scatter_calendar_0, - &INSTANCES.scatter_calendar_1, - &INSTANCES.scatter_calendar_2, - &INSTANCES.scatter_calendar_3, - &INSTANCES.scatter_calendar_4, - &INSTANCES.scatter_calendar_5, - &INSTANCES.scatter_calendar_6, - // Gather calendars - &INSTANCES.gather_calendar_0, - &INSTANCES.gather_calendar_1, - &INSTANCES.gather_calendar_2, - &INSTANCES.gather_calendar_3, - &INSTANCES.gather_calendar_4, - &INSTANCES.gather_calendar_5, - &INSTANCES.gather_calendar_6, - ]; - uwriteln!(uart, " Initializing {} calendars", calendars.len()).unwrap(); - let mut i = 0; - calendars.map(|cal| { - cal.initialize_as_ringbuffer(NUM_ENTRIES); - uwriteln!(uart, " Initialized calendar {}", i).unwrap(); - i += 1; - }); - uwriteln!(uart, "All calendars initialized").unwrap(); -} - #[cfg_attr(not(test), entry)] fn main() -> ! { let mut uart = INSTANCES.uart; @@ -135,12 +98,6 @@ fn main() -> ! { .unwrap(); } uwriteln!(uart, "Printed all hardware UGNs").unwrap(); - - // Initialize scatter/gather calendars with incrementing counters - uwriteln!(uart, "Initializing scatter/gather calendars").unwrap(); - initialize_calendars(&mut uart); - uwriteln!(uart, "All calendars initialized").unwrap(); - uwriteln!(uart, "Calling C..").unwrap(); unsafe { c_main() } } diff --git a/firmware-binaries/demos/soft-ugn-mu/src/ringbuffer_align.c b/firmware-binaries/demos/soft-ugn-mu/src/ringbuffer_align.c index 37d7ed94e..2b62fc483 100644 --- a/firmware-binaries/demos/soft-ugn-mu/src/ringbuffer_align.c +++ b/firmware-binaries/demos/soft-ugn-mu/src/ringbuffer_align.c @@ -4,8 +4,8 @@ #include "hals/soft_ugn_demo_mu/device_instances.h" -#include "bittide_gather.h" -#include "bittide_scatter.h" +#include "bittide_ring_receive.h" +#include "bittide_ring_transmit.h" #include "bittide_timer.h" #include "bittide_uart.h" #include "bittide_ugn.h" @@ -16,21 +16,21 @@ #include "ringbuffer_align.h" // ============================================================================ -// Gather Unit Functions (TX/Outgoing) +// Transmit Ringbuffer Functions (TX/Outgoing) // ============================================================================ -void ringbuffer_set_alignment(GatherUnit gather, +void ringbuffer_set_alignment(TransmitRingbuffer tx_ring, enum RingbufferAlignState state) { uint64_t encoded_msg = (uint64_t)(state); - gather_unit_set_gather_memory_unchecked(gather, 0, - (uint8_t const *)&encoded_msg); + transmit_ringbuffer_set_data_unchecked(tx_ring, 0, + (uint8_t const *)&encoded_msg); } // ============================================================================ -// Scatter Unit Functions (RX/Incoming) +// Receive Ringbuffer Functions (RX/Incoming) // ============================================================================ -bool ringbuffer_find_alignment(ScatterUnit scatter, int16_t buffer_size, +bool ringbuffer_find_alignment(ReceiveRingbuffer rx_ring, int16_t buffer_size, int16_t *found_offset, enum RingbufferAlignState *found_state) { // Initialize outputs @@ -39,11 +39,10 @@ bool ringbuffer_find_alignment(ScatterUnit scatter, int16_t buffer_size, // Scan the entire ringbuffer for (int16_t rx_idx = 0; rx_idx < buffer_size; rx_idx++) { - uint64_t scatter_data; - scatter_unit_get_scatter_memory_unchecked(scatter, rx_idx, - (uint8_t *)&scatter_data); + uint64_t rx_data; + receive_ringbuffer_get_data_unchecked(rx_ring, rx_idx, (uint8_t *)&rx_data); - enum RingbufferAlignState state = (enum RingbufferAlignState)(scatter_data); + enum RingbufferAlignState state = (enum RingbufferAlignState)(rx_data); // Check if we found an alignment message if (state == RINGBUFFER_ALIGN_ANNOUNCE || @@ -58,11 +57,10 @@ bool ringbuffer_find_alignment(ScatterUnit scatter, int16_t buffer_size, } enum RingbufferAlignState -ringbuffer_get_alignment_at_offset(ScatterUnit scatter, int16_t offset) { - uint64_t scatter_data; - scatter_unit_get_scatter_memory_unchecked(scatter, offset, - (uint8_t *)&scatter_data); - return (enum RingbufferAlignState)(scatter_data); +ringbuffer_get_alignment_at_offset(ReceiveRingbuffer rx_ring, int16_t offset) { + uint64_t rx_data; + receive_ringbuffer_get_data_unchecked(rx_ring, offset, (uint8_t *)&rx_data); + return (enum RingbufferAlignState)(rx_data); } // ============================================================================ @@ -85,17 +83,17 @@ void align_ringbuffers(UgnContext *ugn_ctx, int16_t *incoming_offsets, // Step 2: Initialize - Write ALIGNMENT_ANNOUNCE to TX index 0, clear rest for (int32_t port = 0; port < num_ports; port++) { - GatherUnit gather = ugn_ctx->gather_units[port]; + TransmitRingbuffer tx_ring = ugn_ctx->transmit_ringbuffers[port]; // Clear rest of buffer uint64_t empty_msg = (uint64_t)(RINGBUFFER_ALIGN_EMPTY); - for (int16_t i = 1; i < GATHER_UNIT_GATHER_MEMORY_LEN; i++) { - gather_unit_set_gather_memory_unchecked(gather, i, - (uint8_t const *)&empty_msg); + for (int16_t i = 1; i < TRANSMIT_RINGBUFFER_DATA_LEN; i++) { + transmit_ringbuffer_set_data_unchecked(tx_ring, i, + (uint8_t const *)&empty_msg); } // Write ALIGNMENT_ANNOUNCE at index 0 - ringbuffer_set_alignment(gather, RINGBUFFER_ALIGN_ANNOUNCE); + ringbuffer_set_alignment(tx_ring, RINGBUFFER_ALIGN_ANNOUNCE); } // ======================================================================== @@ -112,12 +110,12 @@ void align_ringbuffers(UgnContext *ugn_ctx, int16_t *incoming_offsets, continue; // Already found offset for this port } - ScatterUnit scatter = ugn_ctx->scatter_units[port]; + ReceiveRingbuffer rx_ring = ugn_ctx->receive_ringbuffers[port]; // Use the scan function to search for alignment messages int16_t found_offset; enum RingbufferAlignState found_state; - if (ringbuffer_find_alignment(scatter, 4000, &found_offset, + if (ringbuffer_find_alignment(rx_ring, 4000, &found_offset, &found_state)) { // Found message if (found_state == RINGBUFFER_ALIGN_ACKNOWLEDGE) { @@ -149,8 +147,8 @@ void align_ringbuffers(UgnContext *ugn_ctx, int16_t *incoming_offsets, // Change all outgoing messages to ACKNOWLEDGE for (int32_t port = 0; port < num_ports; port++) { - GatherUnit gather = ugn_ctx->gather_units[port]; - ringbuffer_set_alignment(gather, RINGBUFFER_ALIGN_ACKNOWLEDGE); + TransmitRingbuffer tx_ring = ugn_ctx->transmit_ringbuffers[port]; + ringbuffer_set_alignment(tx_ring, RINGBUFFER_ALIGN_ACKNOWLEDGE); } // Wait for all partners to send ACKNOWLEDGE @@ -163,11 +161,11 @@ void align_ringbuffers(UgnContext *ugn_ctx, int16_t *incoming_offsets, continue; // Already received ACK for this port } - ScatterUnit scatter = ugn_ctx->scatter_units[port]; + ReceiveRingbuffer rx_ring = ugn_ctx->receive_ringbuffers[port]; // Check at the known position for ACKNOWLEDGE enum RingbufferAlignState state = - ringbuffer_get_alignment_at_offset(scatter, incoming_offsets[port]); + ringbuffer_get_alignment_at_offset(rx_ring, incoming_offsets[port]); if (state == RINGBUFFER_ALIGN_ACKNOWLEDGE) { received_ack[port] = true; diff --git a/firmware-binaries/demos/soft-ugn-mu/src/ringbuffer_align.h b/firmware-binaries/demos/soft-ugn-mu/src/ringbuffer_align.h index 27c2cdaf2..b2cd947b1 100644 --- a/firmware-binaries/demos/soft-ugn-mu/src/ringbuffer_align.h +++ b/firmware-binaries/demos/soft-ugn-mu/src/ringbuffer_align.h @@ -15,32 +15,33 @@ #include // ============================================================================ -// Gather Unit Functions (TX/Outgoing) +// Transmit Ringbuffer Functions (TX/Outgoing) // ============================================================================ /** - * @brief Set a gather unit's first address to a specific alignment state. + * @brief Set a transmit ringbuffer's first address to a specific alignment + * state. * - * This writes the encoded alignment state value to index 0 of the gather unit's - * memory, which is the first address that will be transmitted. + * This writes the encoded alignment state value to index 0 of the transmit + * ringbuffer's memory, which is the first address that will be transmitted. * - * @param gather The gather unit to configure + * @param tx_ring The transmit ringbuffer to configure * @param state The alignment state to write at the first address */ -void ringbuffer_set_alignment(GatherUnit gather, +void ringbuffer_set_alignment(TransmitRingbuffer tx_ring, enum RingbufferAlignState state); // ============================================================================ -// Scatter Unit Functions (RX/Incoming) +// Receive Ringbuffer Functions (RX/Incoming) // ============================================================================ /** - * @brief Scan a scatter unit's ringbuffer for alignment state messages. + * @brief Scan a receive ringbuffer for alignment state messages. * - * This function scans the entire scatter unit memory looking for + * This function scans the entire receive ringbuffer memory looking for * RINGBUFFER_ALIGN_ANNOUNCE or RINGBUFFER_ALIGN_ACKNOWLEDGE messages. * - * @param scatter The scatter unit to scan + * @param rx_ring The receive ringbuffer to scan * @param buffer_size The size of the ringbuffer to scan * @param found_offset Pointer to store the offset where a message was found * (set to -1 if not found) @@ -48,19 +49,19 @@ void ringbuffer_set_alignment(GatherUnit gather, * (only valid if found_offset >= 0) * @return bool True if an alignment message was found, false otherwise */ -bool ringbuffer_find_alignment(ScatterUnit scatter, int16_t buffer_size, +bool ringbuffer_find_alignment(ReceiveRingbuffer rx_ring, int16_t buffer_size, int16_t *found_offset, enum RingbufferAlignState *found_state); /** - * @brief Read the alignment state at a specific offset in a scatter unit. + * @brief Read the alignment state at a specific offset in a receive ringbuffer. * - * @param scatter The scatter unit to read from + * @param rx_ring The receive ringbuffer to read from * @param offset The offset to read from * @return enum RingbufferAlignState The alignment state found at that offset */ enum RingbufferAlignState -ringbuffer_get_alignment_at_offset(ScatterUnit scatter, int16_t offset); +ringbuffer_get_alignment_at_offset(ReceiveRingbuffer rx_ring, int16_t offset); // ============================================================================ // Ringbuffer Alignment Protocol @@ -70,7 +71,7 @@ ringbuffer_get_alignment_at_offset(ScatterUnit scatter, int16_t offset); * @brief Align ringbuffers across all ports using a two-phase protocol. * * This function implements a distributed alignment protocol that discovers - * the offset of incoming messages on each port's scatter unit. + * the offset of incoming messages on each port's receive ringbuffer. * * Phase 1: Discovery * - Each node writes RINGBUFFER_ALIGN_ANNOUNCE to TX index 0 @@ -81,7 +82,7 @@ ringbuffer_get_alignment_at_offset(ScatterUnit scatter, int16_t offset); * - Each node changes TX message to RINGBUFFER_ALIGN_ACKNOWLEDGE * - Each node waits for all partners to send ACKNOWLEDGE at known positions * - * @param ugn_ctx The UGN context containing scatter and gather units + * @param ugn_ctx The UGN context containing receive and transmit ringbuffers * @param incoming_offsets Array to store discovered offsets for each port * @param uart UART for debug output */ diff --git a/firmware-binaries/sim-tests/ringbuffer_test/src/main.rs b/firmware-binaries/sim-tests/ringbuffer_test/src/main.rs index db7d92234..d69ade07e 100644 --- a/firmware-binaries/sim-tests/ringbuffer_test/src/main.rs +++ b/firmware-binaries/sim-tests/ringbuffer_test/src/main.rs @@ -5,8 +5,8 @@ #![cfg_attr(not(test), no_main)] use bittide_hal::{ - manual_additions::timer::Duration, ringbuffer_test::DeviceInstances, - shared_devices::TransmitRingbuffer, + manual_additions::timer::Duration, + ringbuffer_test::{devices::TransmitRingbuffer, DeviceInstances}, }; use core::fmt::Write; #[cfg(not(test))] diff --git a/firmware-support/bittide-hal-c/include/bittide_gather.h b/firmware-support/bittide-hal-c/include/bittide_gather.h index 106a5a21e..36d6db23c 100644 --- a/firmware-support/bittide-hal-c/include/bittide_gather.h +++ b/firmware-support/bittide-hal-c/include/bittide_gather.h @@ -22,8 +22,6 @@ #ifdef HAL_SCATTER_GATHER_PE_DEVICE_GATHER_UNIT_H #include "hals/scatter_gather_pe/devices/gather_unit.h" -#elif defined HAL_SOFT_UGN_DEMO_MU_DEVICE_GATHER_UNIT_H -#include "hals/soft_ugn_demo_mu/devices/gather_unit.h" #elif defined HAL_SWITCH_DEMO_GPPE_PE_DEVICE_GATHER_UNIT_H #include "hals/switch_demo_gppe_pe/devices/gather_unit.h" #else @@ -74,6 +72,7 @@ static inline bool gather_unit_write_slice(GatherUnit unit, uint64_t *src, return true; } + /** * Write a slice of data to gather memory with wrapping * @@ -102,6 +101,7 @@ static inline void gather_unit_write_slice_wrapping(GatherUnit unit, second_part_len); } } + /** * Wait for the next metacycle boundary * @@ -126,4 +126,5 @@ static inline void gather_unit_clear(GatherUnit unit) { gather_unit_set_gather_memory_unchecked(unit, i, 0); } } + #endif // BITTIDE_GATHER_H diff --git a/firmware-support/bittide-hal-c/include/bittide_ring_receive.h b/firmware-support/bittide-hal-c/include/bittide_ring_receive.h new file mode 100644 index 000000000..b490fbf51 --- /dev/null +++ b/firmware-support/bittide-hal-c/include/bittide_ring_receive.h @@ -0,0 +1,127 @@ +// SPDX-FileCopyrightText: 2025 Google LLC +// +// SPDX-License-Identifier: Apache-2.0 + +/// This file contains helper functions for working with scatter units / receive +/// ringbuffers. +/// +/// Because there are multiple different implementations of scatter units +/// (due to slight differences in register types) this code would need to be +/// generic. +/// As C doesn't have generics, this file instead depends on the specific +/// scatter unit header file to be included to use the correct interface. + +#ifndef BITTIDE_RING_RECEIVE +#define BITTIDE_RING_RECEIVE + +#include "stdbool.h" +#include "stdint.h" +#include "string.h" + +// All of those includes don't really do anything.. I guess it might help +// with auto-completion though? + +#ifdef HAL_SOFT_UGN_DEMO_MU_DEVICE_RECEIVE_RINGBUFFER_H +#include "hals/soft_ugn_demo_mu/devices/receive_ringbuffer.h" +// Type, constant, and functions already defined in receive_ringbuffer.h +#else +#error "No receive ringbuffer header definition found!" +#endif + +/** + * Read a slice of data from receive ringbuffer memory + * + * Reads data from the receive memory buffer without performing bounds checking. + * + * @param unit Pointer to the ReceiveRingbuffer + * @param dst Destination buffer to store read data (must have space for at + * least len words) + * @param offset Offset in receive memory (in uint64_t words) to start reading + * from + * @param len Number of uint64_t words to read + */ +static inline void +receive_ringbuffer_read_slice_unchecked(ReceiveRingbuffer unit, uint64_t *dst, + uint32_t offset, uint32_t len) { +#ifdef HAL_SOFT_UGN_DEMO_MU_DEVICE_RECEIVE_RINGBUFFER_H + // For soft_ugn_demo_mu: use byte-oriented API with 8-byte chunks + for (uint32_t i = 0; i < len; i++) { + receive_ringbuffer_get_data_unchecked(unit, offset + i, (uint8_t *)&dst[i]); + } +#else + // For scatter/gather units: use direct memory access + memcpy_volatile(dst, (unit.base + 0) + (offset * sizeof(uint64_t)), + len * sizeof(uint64_t)); +#endif +} + +/** + * Read a slice of data from receive ringbuffer memory + * + * Reads data from the receive memory buffer. This function performs bounds + * checking to prevent reading beyond the allocated memory region. + * + * @param unit Pointer to the ReceiveRingbuffer + * @param dst Destination buffer to store read data (must have space for at + * least len words) + * @param offset Offset in receive memory (in uint64_t words) to start reading + * from + * @param len Number of uint64_t words to read + * @return true on success, false if bounds check fails or parameters are + * invalid + */ +static inline bool receive_ringbuffer_read_slice(ReceiveRingbuffer unit, + uint64_t *dst, uint32_t offset, + uint32_t len) { + // Validate parameters + if (dst == 0 || offset + len > RECEIVE_RINGBUFFER_DATA_LEN) { + return false; + } + + receive_ringbuffer_read_slice_unchecked(unit, dst, offset, len); + + return true; +} + +/** + * Read a slice of data from receive ringbuffer memory with wrapping + * + * This function reads data from the receive memory, wrapping around to the + * beginning of the memory if the read operation exceeds the memory boundary. + * + * @param unit Pointer to the ReceiveRingbuffer + * @param dst Destination buffer to store read data + * @param offset Offset in receive memory (in uint64_t words) to start reading + * from + * @param len Number of uint64_t words to read + */ +static inline void +receive_ringbuffer_read_slice_wrapping(ReceiveRingbuffer unit, uint64_t *dst, + uint32_t offset, uint32_t len) { + if (offset + len <= RECEIVE_RINGBUFFER_DATA_LEN) { + // No wrapping needed + receive_ringbuffer_read_slice_unchecked(unit, dst, offset, len); + } else { + // Wrapping needed + uint32_t first_part_len = RECEIVE_RINGBUFFER_DATA_LEN - offset; + uint32_t second_part_len = len - first_part_len; + + receive_ringbuffer_read_slice_unchecked(unit, dst, offset, first_part_len); + receive_ringbuffer_read_slice_unchecked(unit, dst + first_part_len, 0, + second_part_len); + } +} + +/** + * Clear the receive ringbuffer memory by setting all words to zero + * Note: This function is generally not needed for receive buffers as they are + * read-only. + * + * @param unit Pointer to the ReceiveRingbuffer + */ +static inline void receive_ringbuffer_clear(ReceiveRingbuffer unit) { + (void)unit; + // Receive ringbuffers are typically read-only, so clearing is not applicable +} + +#endif // BITTIDE_RING_RECEIVE diff --git a/firmware-support/bittide-hal-c/include/bittide_ring_transmit.h b/firmware-support/bittide-hal-c/include/bittide_ring_transmit.h new file mode 100644 index 000000000..359015a52 --- /dev/null +++ b/firmware-support/bittide-hal-c/include/bittide_ring_transmit.h @@ -0,0 +1,120 @@ +// SPDX-FileCopyrightText: 2025 Google LLC +// +// SPDX-License-Identifier: Apache-2.0 + +/// This file contains helper functions for working with gather units. +/// +/// Because there are multiple different implementations of gather units +/// (due to slight differences in register types) this code would need to be +/// generic. +/// As C doesn't have generics, this file instead depends on the specific +/// gather unit header file to be included to use the correct interface. + +#ifndef BITTIDE_RING_TRANSMIT +#define BITTIDE_RING_TRANSMIT + +#include "stdbool.h" +#include "stdint.h" +#include "string.h" + +// All of those includes don't really do anything.. I guess it might help +// with auto-completion though? + +#ifdef HAL_SOFT_UGN_DEMO_MU_DEVICE_TRANSMIT_RINGBUFFER_H +#include "hals/soft_ugn_demo_mu/devices/transmit_ringbuffer.h" +// Type, constant, and functions already defined in transmit_ringbuffer.h +#else +#error "No transmit ringbuffer header definition found!" +#endif + +/** + * Write a slice of data to transmit ringbuffer memory + * + * Writes data to the transmit memory buffer without performing bounds checking. + * + * @param unit Pointer to the TransmitRingbuffer + * @param src Source buffer containing data to write (must contain at least len + * words) + * @param offset Offset in transmit memory (in uint64_t words) to start writing + * to + * @param len Number of uint64_t words to write + */ +static inline void transmit_ringbuffer_write_slice_unchecked( + TransmitRingbuffer unit, uint64_t *src, uint32_t offset, uint32_t len) { + for (uint32_t i = 0; i < len; i++) { + transmit_ringbuffer_set_data_unchecked(unit, offset + i, + (uint8_t const *)&src[i]); + } +} + +/** + * Write a slice of data to transmit ringbuffer memory + * + * Writes data to the transmit memory buffer. This function performs bounds + * checking to prevent writing beyond the allocated memory region. + * + * @param unit Pointer to the TransmitRingbuffer + * @param src Source buffer containing data to write (must contain at least len + * words) + * @param offset Offset in transmit memory (in uint64_t words) to start writing + * to + * @param len Number of uint64_t words to write + * @return true on success, false if bounds check fails or parameters are + * invalid + */ +static inline bool transmit_ringbuffer_write_slice(TransmitRingbuffer unit, + uint64_t *src, + uint32_t offset, + uint32_t len) { + // Validate parameters + if (src == 0 || offset + len > TRANSMIT_RINGBUFFER_DATA_LEN) { + return false; + } + + transmit_ringbuffer_write_slice_unchecked(unit, src, offset, len); + + return true; +} +/** + * Write a slice of data to transmit ringbuffer memory with wrapping + * + * This function writes data to the transmit memory, wrapping around to the + * beginning of the memory if the write operation exceeds the memory boundary. + * + * @param unit Pointer to the TransmitRingbuffer + * @param src Source buffer containing data to write + * @param offset Offset in transmit memory (in uint64_t words) to start writing + * to + * @param len Number of uint64_t words to write + */ +static inline void +transmit_ringbuffer_write_slice_wrapping(TransmitRingbuffer unit, uint64_t *src, + uint32_t offset, uint32_t len) { + if (offset + len <= TRANSMIT_RINGBUFFER_DATA_LEN) { + // No wrapping needed + transmit_ringbuffer_write_slice_unchecked(unit, src, offset, len); + } else { + // Wrapping needed + uint32_t first_part_len = TRANSMIT_RINGBUFFER_DATA_LEN - offset; + uint32_t second_part_len = len - first_part_len; + + transmit_ringbuffer_write_slice_unchecked(unit, src, offset, + first_part_len); + transmit_ringbuffer_write_slice_unchecked(unit, src + first_part_len, 0, + second_part_len); + } +} + +/** + * Clear the transmit ringbuffer memory by setting all words to zero + * + * @param unit Pointer to the TransmitRingbuffer + */ +static inline void transmit_ringbuffer_clear(TransmitRingbuffer unit) { + // For soft_ugn_demo_mu: clear byte-oriented ringbuffer + uint8_t zeros[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + for (uint32_t i = 0; i < TRANSMIT_RINGBUFFER_DATA_LEN; i++) { + transmit_ringbuffer_set_data_unchecked(unit, i, zeros); + } +} +#endif diff --git a/firmware-support/bittide-hal-c/include/bittide_scatter.h b/firmware-support/bittide-hal-c/include/bittide_scatter.h index 7d22fe6fc..d84c3cbad 100644 --- a/firmware-support/bittide-hal-c/include/bittide_scatter.h +++ b/firmware-support/bittide-hal-c/include/bittide_scatter.h @@ -22,8 +22,6 @@ #ifdef HAL_SCATTER_GATHER_PE_DEVICE_SCATTER_UNIT_H #include "hals/scatter_gather_pe/devices/scatter_unit.h" -#elif defined HAL_SOFT_UGN_DEMO_MU_DEVICE_SCATTER_UNIT_H -#include "hals/soft_ugn_demo_mu/devices/scatter_unit.h" #elif defined HAL_SWITCH_DEMO_GPPE_PE_DEVICE_SCATTER_UNIT_H #include "hals/switch_demo_gppe_pe/devices/scatter_unit.h" #else diff --git a/firmware-support/bittide-hal-c/include/bittide_ugn.h b/firmware-support/bittide-hal-c/include/bittide_ugn.h index 9f04dc6e6..d9d9f4c45 100644 --- a/firmware-support/bittide-hal-c/include/bittide_ugn.h +++ b/firmware-support/bittide-hal-c/include/bittide_ugn.h @@ -86,9 +86,9 @@ typedef struct { // ============================================================================ // Holds all state needed for UGN discovery protocol typedef struct { - // Scatter and gather units (one per port) - ScatterUnit *scatter_units; - GatherUnit *gather_units; + // Receive and transmit ringbuffers (one per port) + ReceiveRingbuffer *receive_ringbuffers; + TransmitRingbuffer *transmit_ringbuffers; uint32_t num_ports; // UGN edge lists (dynamically sized based on max_degree) @@ -194,14 +194,13 @@ static inline void ugn_edge_init(UgnEdge *edge) { // ============================================================================ // Initialize UGN protocol context -static inline void ugn_context_init(UgnContext *ctx, ScatterUnit *scatter_units, - GatherUnit *gather_units, - uint32_t num_ports, uint32_t node_id, - UgnEdge *incoming_list, - UgnEdge *outgoing_list, - uint32_t max_degree) { - ctx->scatter_units = scatter_units; - ctx->gather_units = gather_units; +static inline void +ugn_context_init(UgnContext *ctx, ReceiveRingbuffer *receive_ringbuffers, + TransmitRingbuffer *transmit_ringbuffers, uint32_t num_ports, + uint32_t node_id, UgnEdge *incoming_list, + UgnEdge *outgoing_list, uint32_t max_degree) { + ctx->receive_ringbuffers = receive_ringbuffers; + ctx->transmit_ringbuffers = transmit_ringbuffers; ctx->num_ports = num_ports; ctx->node_id = node_id; ctx->incoming_link_ugn_list = incoming_list; @@ -250,22 +249,22 @@ static inline bool all_ports_done(UgnContext *ctx) { // Future changes to the offset calculation (e.g., for non-trivial calendar // configurations) only need to be updated here. -// Calculate scatter buffer offset for a given event time and port -static inline uint32_t ugn_calculate_scatter_offset(const UgnContext *ctx, +// Calculate receive buffer offset for a given event time and port +static inline uint32_t ugn_calculate_receive_offset(const UgnContext *ctx, uint32_t port, uint64_t event_time) { (void)ctx; (void)port; // Unused in this implementation - return (uint32_t)(event_time % SCATTER_UNIT_SCATTER_MEMORY_LEN); + return (uint32_t)(event_time % RECEIVE_RINGBUFFER_DATA_LEN); } -// Calculate gather buffer offset for a given event time and port -static inline uint32_t ugn_calculate_gather_offset(const UgnContext *ctx, - uint32_t port, - uint64_t event_time) { +// Calculate transmit buffer offset for a given event time and port +static inline uint32_t ugn_calculate_transmit_offset(const UgnContext *ctx, + uint32_t port, + uint64_t event_time) { (void)ctx; (void)port; // Unused in this implementation - return (uint32_t)(event_time % GATHER_UNIT_GATHER_MEMORY_LEN); + return (uint32_t)(event_time % TRANSMIT_RINGBUFFER_DATA_LEN); } // ============================================================================ @@ -292,11 +291,11 @@ static inline uint32_t ugn_decode_port(uint64_t encoded) { return port_plus_one - 1; } -// Read a complete message from scatter buffer -static inline bool ugn_read_message(ScatterUnit *unit, uint32_t offset, +// Read a complete message from receive buffer +static inline bool ugn_read_message(ReceiveRingbuffer *unit, uint32_t offset, UgnMessage *msg) { uint64_t data[6]; - scatter_unit_read_slice_wrapping(*unit, data, offset, 6); + receive_ringbuffer_read_slice_wrapping(*unit, data, offset, 6); if (data[0] != BT_MAGIC_LO || data[1] != BT_MAGIC_HI) { return false; @@ -349,10 +348,10 @@ static inline bool process_ugn_message(const UgnMessage *msg, // Send UGN to a specific port static inline bool send_ugn_to_port(UgnContext *ctx, Timer timer, Event *event) { - GatherUnit *unit = &ctx->gather_units[event->port]; + TransmitRingbuffer *unit = &ctx->transmit_ringbuffers[event->port]; uint32_t port = event->port; - uint32_t offset = ugn_calculate_gather_offset(ctx, port, event->event_time); - uint64_t deadline = event->event_time + GATHER_UNIT_GATHER_MEMORY_LEN; + uint32_t offset = ugn_calculate_transmit_offset(ctx, port, event->event_time); + uint64_t deadline = event->event_time + TRANSMIT_RINGBUFFER_DATA_LEN; if (port >= ctx->num_ports) return false; @@ -380,7 +379,7 @@ static inline bool send_ugn_to_port(UgnContext *ctx, Timer timer, buffer[5] = ugn_encode_node_port(msg.node_id, msg.port); timer_wait_until_cycles(timer, event->event_time); - gather_unit_write_slice_wrapping(*unit, buffer, offset, 6); + transmit_ringbuffer_write_slice_wrapping(*unit, buffer, offset, 6); CompareResult cmp_result = timer_compare_cycles(timer, deadline); if (cmp_result == COMPARE_LESS) { @@ -399,7 +398,7 @@ static inline bool send_ugn_to_port(UgnContext *ctx, Timer timer, // Check incoming buffer for a specific port static inline bool check_incoming_buffer(UgnContext *ctx, Timer timer, Event *event) { - ScatterUnit *unit = &ctx->scatter_units[event->port]; + ReceiveRingbuffer *unit = &ctx->receive_ringbuffers[event->port]; if (event->port >= ctx->num_ports) return false; @@ -412,8 +411,8 @@ static inline bool check_incoming_buffer(UgnContext *ctx, Timer timer, UgnEdge edge_in; UgnEdge edge_out; uint32_t offset = - ugn_calculate_scatter_offset(ctx, event->port, event->event_time); - uint64_t deadline = event->event_time + SCATTER_UNIT_SCATTER_MEMORY_LEN; + ugn_calculate_receive_offset(ctx, event->port, event->event_time); + uint64_t deadline = event->event_time + RECEIVE_RINGBUFFER_DATA_LEN; timer_wait_until_cycles(timer, event->event_time); bool message_available = ugn_read_message(unit, offset, &msg); @@ -446,12 +445,12 @@ static inline bool check_incoming_buffer(UgnContext *ctx, Timer timer, return (cmp_result == COMPARE_LESS); } -// Invalidate old scatter buffer data for a specific port +// Invalidate old receive buffer data for a specific port static inline void invalidate_port(UgnContext *ctx, Timer timer, Event *event) { uint32_t port = event->port; - GatherUnit *gather_unit = &ctx->gather_units[port]; - uint64_t deadline = event->event_time + GATHER_UNIT_GATHER_MEMORY_LEN; - uint32_t offset = ugn_calculate_gather_offset(ctx, port, event->event_time); + TransmitRingbuffer *transmit_unit = &ctx->transmit_ringbuffers[port]; + uint64_t deadline = event->event_time + TRANSMIT_RINGBUFFER_DATA_LEN; + uint32_t offset = ugn_calculate_transmit_offset(ctx, port, event->event_time); if (port >= ctx->num_ports) return; @@ -459,7 +458,7 @@ static inline void invalidate_port(UgnContext *ctx, Timer timer, Event *event) { uint64_t zeros[6] = {0, 0, 0, 0, 0, 0}; timer_wait_until_cycles(timer, event->event_time); - gather_unit_write_slice_wrapping(*gather_unit, zeros, offset, 6); + transmit_ringbuffer_write_slice_wrapping(*transmit_unit, zeros, offset, 6); CompareResult cmp_result = timer_compare_cycles(timer, deadline); if (cmp_result == COMPARE_LESS) { diff --git a/firmware-support/bittide-hal/src/manual_additions/calendar.rs b/firmware-support/bittide-hal/src/manual_additions/calendar.rs index 6b355d46b..74841790b 100644 --- a/firmware-support/bittide-hal/src/manual_additions/calendar.rs +++ b/firmware-support/bittide-hal/src/manual_additions/calendar.rs @@ -54,12 +54,6 @@ impl_valid_entry_type! { mask: 12, } -impl_valid_entry_type! { - type: [crate::types::ValidEntry_16], - repeat: u16, - mask: 16, -} - /// Abstraction trait over all the methods that a calendar type should provide pub trait CalendarInterface { type EntryType: Copy + ValidEntryType; @@ -317,15 +311,6 @@ impl_calendar_interface! { entry: crate::types::ValidEntry_12, } -impl_calendar_interface! { - cal: crate::hals::soft_ugn_demo_mu::devices::Calendar, - metacycle: u32, - shadow: u16, - write: u16, - read: u16, - entry: crate::types::ValidEntry_16, -} - pub trait RingbufferCalendar { fn initialize_as_ringbuffer(&self, size: usize); } diff --git a/firmware-support/bittide-hal/src/manual_additions/soft_ugn_demo_mu/scatter_gather.rs b/firmware-support/bittide-hal/src/manual_additions/soft_ugn_demo_mu/scatter_gather.rs index 2862ede62..46b60d165 100644 --- a/firmware-support/bittide-hal/src/manual_additions/soft_ugn_demo_mu/scatter_gather.rs +++ b/firmware-support/bittide-hal/src/manual_additions/soft_ugn_demo_mu/scatter_gather.rs @@ -1,60 +1,42 @@ // SPDX-FileCopyrightText: 2025 Google LLC // // SPDX-License-Identifier: Apache-2.0 -use crate::soft_ugn_demo_mu::devices::{GatherUnit, ScatterUnit}; +use crate::soft_ugn_demo_mu::devices::{ReceiveRingbuffer, TransmitRingbuffer}; -impl GatherUnit { - /// Write a slice to the gather memory. +impl TransmitRingbuffer { + /// Write a slice to the transmit ringbuffer memory. /// /// # Panics /// /// The source memory size must be smaller or equal to the memory size of - /// the `GatherUnit` memory. + /// the `TransmitRingbuffer` memory. pub fn write_slice(&self, src: &[[u8; 8]], offset: usize) { - assert!(src.len() + offset <= Self::GATHER_MEMORY_LEN); + assert!(src.len() + offset <= Self::DATA_LEN); let mut off = offset; for &val in src { unsafe { - self.set_gather_memory_unchecked(off, val); + self.set_data_unchecked(off, val); } off += 1; } } - - /// Wait for the start of a new metacycle. - /// - /// Reading from the register will cause a stall until the end of the - /// metacycle. The read value is not actually relevant, so it's safe - /// to discard. - pub fn wait_for_new_metacycle(&self) { - let _ = self.metacycle_register(); - } } -impl ScatterUnit { - /// Read a slice from the scatter memory. +impl ReceiveRingbuffer { + /// Read a slice from the receive ringbuffer memory. /// /// # Panics /// /// The destination memory size must be smaller or equal to the memory size - /// of the `ScatterUnit`. + /// of the `ReceiveRingbuffer`. pub fn read_slice(&self, dst: &mut [[u8; 8]], offset: usize) { - assert!(dst.len() + offset <= Self::SCATTER_MEMORY_LEN); + assert!(dst.len() + offset <= Self::DATA_LEN); let mut off = offset; for val in dst { unsafe { - *val = self.scatter_memory_unchecked(off); + *val = self.data_unchecked(off); } off += 1; } } - - /// Wait for the start of a new metacycle. - /// - /// Reading from the register will cause a stall until the end of the - /// metacycle. The read value is not actually relevant, so it's safe - /// to discard. - pub fn wait_for_new_metacycle(&self) { - let _ = self.metacycle_register(); - } }