change MemoryInterface types to have their own config

This commit is contained in:
Jacob Lifshay 2026-02-27 17:26:07 -08:00
parent 7f7e316b7b
commit 2c8de5bec4
Signed by: programmerjake
SSH key fingerprint: SHA256:HnFTLGpSm4Q4Fj502oCFisjZSoakwEuTsJJMSke63RQ
7 changed files with 9358 additions and 23280 deletions

View file

@ -9,8 +9,9 @@ use crate::{
CpuConfigMaxFetchesInFlight, PhantomConstCpuConfig,
},
main_memory_and_io::{
MemoryInterface, MemoryOperationErrorKind, MemoryOperationFinish,
MemoryOperationFinishKind, MemoryOperationKind, MemoryOperationStart,
CpuConfigFetchMemoryInterfaceConfig, MemoryInterface, MemoryInterfaceConfig,
MemoryOperationErrorKind, MemoryOperationFinish, MemoryOperationFinishKind,
MemoryOperationKind, MemoryOperationStart,
},
next_pc::{
FETCH_BLOCK_ID_WIDTH, NextPcToFetchInterface, NextPcToFetchInterfaceInner, ResetStatus,
@ -415,7 +416,9 @@ impl<C: PhantomConstCpuConfig> L1ICacheStateSim<C> {
self.state_expr.ty().config
}
#[hdl]
fn try_start_memory_operation(&mut self) -> Option<SimValue<MemoryOperationStart<C>>> {
fn try_start_memory_operation(
&mut self,
) -> Option<SimValue<MemoryOperationStart<CpuConfigFetchMemoryInterfaceConfig<C>>>> {
let config = self.config();
for cache_miss in &mut self.cache_misses {
let Some(next_start_fetch_block) = CacheMiss::next_start_fetch_block(cache_miss) else {
@ -430,7 +433,7 @@ impl<C: PhantomConstCpuConfig> L1ICacheStateSim<C> {
error: _, // handled by CacheMiss::next_start_fetch_block()
config: _,
} = cache_miss;
let mem_op_ty = MemoryOperationStart[config];
let mem_op_ty = MemoryOperationStart[CpuConfigFetchMemoryInterfaceConfig[config]];
let mut addr = SplitAddr[config].split_addr_sim(addr);
#[hdl(sim)]
let SplitAddr::<_> {
@ -453,8 +456,8 @@ impl<C: PhantomConstCpuConfig> L1ICacheStateSim<C> {
mem_op_ty.write_data.len(),
),
rw_mask: repeat(true, mem_op_ty.write_data.len()),
fetch_block_id,
config,
op_id: SimValue::into_dyn_int(fetch_block_id.clone()),
config: CpuConfigFetchMemoryInterfaceConfig[config],
},
);
}
@ -497,7 +500,9 @@ impl<C: PhantomConstCpuConfig> L1ICacheStateSim<C> {
fn do_memory_operation_finish<'a>(
&mut self,
ready_for_memory_operation_finish: ReadyForMemoryOperationFinish,
memory_operation_finish: impl ToSimValue<Type = MemoryOperationFinish<C>>,
memory_operation_finish: impl ToSimValue<
Type = MemoryOperationFinish<CpuConfigFetchMemoryInterfaceConfig<C>>,
>,
) -> Option<SimValue<WriteBackStep<C>>> {
let config = self.config();
let cache_miss = &mut self.cache_misses[ready_for_memory_operation_finish.cache_miss_index];
@ -621,9 +626,7 @@ impl<C: PhantomConstCpuConfig> L1ICacheStateSim<C> {
#[hdl]
fn check_memory_next_fetch_block_ids(
&self,
memory_next_fetch_block_ids: SimValue<
ArrayVec<UInt<{ FETCH_BLOCK_ID_WIDTH }>, CpuConfigMaxFetchesInFlight<C>>,
>,
memory_next_fetch_block_ids: SimValue<ArrayVec<UInt, DynSize>>,
) {
let memory_next_fetch_block_ids = ArrayVec::elements_sim_ref(&memory_next_fetch_block_ids);
let mut expected_memory_next_fetch_block_ids = Vec::new();
@ -1071,8 +1074,8 @@ fn l1_i_cache_impl(config: PhantomConst<CpuConfig>) {
#[hdl]
let cd: ClockDomain = m.input();
#[hdl]
let memory_interface: MemoryInterface<PhantomConst<CpuConfig>> =
m.output(MemoryInterface[config]);
let memory_interface: MemoryInterface<PhantomConst<MemoryInterfaceConfig>> =
m.output(MemoryInterface[CpuConfigFetchMemoryInterfaceConfig[config]]);
#[hdl]
let from_next_pc: NextPcToFetchInterface<PhantomConst<CpuConfig>> =
m.input(NextPcToFetchInterface[config]);
@ -1103,7 +1106,7 @@ fn l1_i_cache_impl(config: PhantomConst<CpuConfig>) {
async fn run(
mut sim: ExternModuleSimulationState,
cd: Expr<ClockDomain>,
memory_interface: Expr<MemoryInterface<PhantomConst<CpuConfig>>>,
memory_interface: Expr<MemoryInterface<PhantomConst<MemoryInterfaceConfig>>>,
from_next_pc: Expr<NextPcToFetchInterface<PhantomConst<CpuConfig>>>,
to_decode_fetched: Expr<ReadyValid<FetchToDecodeInterfaceInner<PhantomConst<CpuConfig>>>>,
to_decode_next_fetch_block_ids: Expr<
@ -1196,9 +1199,8 @@ fn l1_i_cache_impl(config: PhantomConst<CpuConfig>) {
)
.await;
#[hdl(sim)]
if let HdlSome(next_fetch_block_ids) = sim
.read_past(memory_interface.next_fetch_block_ids, cd.clk)
.await
if let HdlSome(next_fetch_block_ids) =
sim.read_past(memory_interface.next_op_ids, cd.clk).await
{
state.check_memory_next_fetch_block_ids(next_fetch_block_ids);
}
@ -1359,7 +1361,7 @@ fn l1_i_cache_impl(config: PhantomConst<CpuConfig>) {
state_for_debug,
),
mut sim| async move {
let config = memory_interface.ty().config;
let config = from_next_pc.ty().config;
let cache_line_ty = CacheLine[config];
sim.write(i_cache_port.clk, false).await; // externally overridden with cd.clk, so just write a constant here
sim.resettable(
@ -1426,8 +1428,8 @@ pub fn l1_i_cache(config: PhantomConst<CpuConfig>) {
#[hdl]
let cd: ClockDomain = m.input();
#[hdl]
let memory_interface: MemoryInterface<PhantomConst<CpuConfig>> =
m.output(MemoryInterface[config]);
let memory_interface: MemoryInterface<PhantomConst<MemoryInterfaceConfig>> =
m.output(MemoryInterface[CpuConfigFetchMemoryInterfaceConfig[config]]);
#[hdl]
let from_next_pc: NextPcToFetchInterface<PhantomConst<CpuConfig>> =
m.input(NextPcToFetchInterface[config]);
@ -1524,8 +1526,8 @@ pub fn fetch(config: PhantomConst<CpuConfig>) {
#[hdl]
let cd: ClockDomain = m.input();
#[hdl]
let memory_interface: MemoryInterface<PhantomConst<CpuConfig>> =
m.output(MemoryInterface[config]);
let memory_interface: MemoryInterface<PhantomConst<MemoryInterfaceConfig>> =
m.output(MemoryInterface[CpuConfigFetchMemoryInterfaceConfig[config]]);
#[hdl]
let from_next_pc: NextPcToFetchInterface<PhantomConst<CpuConfig>> =
m.input(NextPcToFetchInterface[config]);

View file

@ -1,17 +1,53 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
config::{
CpuConfig, CpuConfigFetchWidthInBytes, CpuConfigMaxFetchesInFlight, PhantomConstCpuConfig,
},
next_pc::FETCH_BLOCK_ID_WIDTH,
util::array_vec::ArrayVec,
};
use crate::{config::CpuConfig, next_pc::FETCH_BLOCK_ID_WIDTH, util::array_vec::ArrayVec};
use fayalite::{prelude::*, util::ready_valid::ReadyValid};
use std::num::NonZeroUsize;
pub mod simple_uart;
#[derive(Clone, Eq, PartialEq, Hash, Debug, serde::Serialize, serde::Deserialize)]
#[non_exhaustive]
pub struct MemoryInterfaceConfig {
pub log2_bus_width_in_bytes: u8,
pub queue_capacity: NonZeroUsize,
pub op_id_width: usize,
}
impl MemoryInterfaceConfig {
pub const fn from_cpu_config_for_fetch(config: &CpuConfig) -> Self {
Self {
log2_bus_width_in_bytes: config.log2_fetch_width_in_bytes,
queue_capacity: config.max_fetches_in_flight,
op_id_width: FETCH_BLOCK_ID_WIDTH,
}
}
pub const fn bus_width_in_bytes(&self) -> usize {
1usize.strict_shl(self.log2_bus_width_in_bytes as u32)
}
}
#[hdl(get(|c| PhantomConst::new_sized(MemoryInterfaceConfig::from_cpu_config_for_fetch(&c))))]
pub type CpuConfigFetchMemoryInterfaceConfig<C: PhantomConstGet<CpuConfig>> =
PhantomConst<MemoryInterfaceConfig>;
#[hdl(get(|c| c.log2_bus_width_in_bytes as usize))]
pub type MemoryInterfaceLog2BusWidthInBytes<C: PhantomConstGet<MemoryInterfaceConfig>> = DynSize;
#[hdl(get(|c| c.bus_width_in_bytes()))]
pub type MemoryInterfaceBusWidthInBytes<C: PhantomConstGet<MemoryInterfaceConfig>> = DynSize;
#[hdl(get(|c| c.queue_capacity.get()))]
pub type MemoryInterfaceQueueCapacity<C: PhantomConstGet<MemoryInterfaceConfig>> = DynSize;
#[hdl(get(|c| c.op_id_width))]
pub type MemoryInterfaceOpIdWidth<C: PhantomConstGet<MemoryInterfaceConfig>> = DynSize;
#[hdl]
pub type MemoryInterfaceOpId<C: PhantomConstGet<MemoryInterfaceConfig>> =
UIntType<MemoryInterfaceOpIdWidth<C>>;
#[hdl]
pub enum MemoryOperationKind {
Read,
@ -19,12 +55,12 @@ pub enum MemoryOperationKind {
}
#[hdl(no_static)]
pub struct MemoryOperationStart<C: PhantomConstGet<CpuConfig> + PhantomConstCpuConfig> {
pub struct MemoryOperationStart<C: PhantomConstGet<MemoryInterfaceConfig>> {
pub kind: MemoryOperationKind,
pub addr: UInt<64>,
pub write_data: ArrayType<UInt<8>, CpuConfigFetchWidthInBytes<C>>,
pub rw_mask: ArrayType<Bool, CpuConfigFetchWidthInBytes<C>>,
pub fetch_block_id: UInt<{ FETCH_BLOCK_ID_WIDTH }>, // for debugging
pub write_data: ArrayType<UInt<8>, MemoryInterfaceBusWidthInBytes<C>>,
pub rw_mask: ArrayType<Bool, MemoryInterfaceBusWidthInBytes<C>>,
pub op_id: MemoryInterfaceOpId<C>, // for debugging
pub config: C,
}
@ -40,20 +76,19 @@ pub enum MemoryOperationFinishKind {
}
#[hdl(no_static)]
pub struct MemoryOperationFinish<C: PhantomConstGet<CpuConfig> + PhantomConstCpuConfig> {
pub struct MemoryOperationFinish<C: PhantomConstGet<MemoryInterfaceConfig>> {
pub kind: MemoryOperationFinishKind,
pub read_data: ArrayType<UInt<8>, CpuConfigFetchWidthInBytes<C>>,
pub read_data: ArrayType<UInt<8>, MemoryInterfaceBusWidthInBytes<C>>,
pub config: C,
}
#[hdl(no_static)]
pub struct MemoryInterface<C: PhantomConstGet<CpuConfig> + PhantomConstCpuConfig> {
pub struct MemoryInterface<C: PhantomConstGet<MemoryInterfaceConfig>> {
pub start: ReadyValid<MemoryOperationStart<C>>,
#[hdl(flip)]
pub finish: ReadyValid<MemoryOperationFinish<C>>,
/// for debugging
#[hdl(flip)]
pub next_fetch_block_ids:
HdlOption<ArrayVec<UInt<{ FETCH_BLOCK_ID_WIDTH }>, CpuConfigMaxFetchesInFlight<C>>>,
pub next_op_ids: HdlOption<ArrayVec<MemoryInterfaceOpId<C>, MemoryInterfaceQueueCapacity<C>>>,
pub config: C,
}

View file

@ -4,9 +4,8 @@
use std::num::NonZeroUsize;
use crate::{
config::{CpuConfig, PhantomConstCpuConfig},
main_memory_and_io::{
MemoryInterface, MemoryOperationErrorKind, MemoryOperationFinish,
MemoryInterface, MemoryInterfaceConfig, MemoryOperationErrorKind, MemoryOperationFinish,
MemoryOperationFinishKind, MemoryOperationKind, MemoryOperationStart,
},
util::array_vec::ArrayVec,
@ -358,25 +357,38 @@ pub fn receiver(queue_capacity: NonZeroUsize) {
pub const SIMPLE_UART_RECEIVE_OFFSET: u64 = 0;
pub const SIMPLE_UART_TRANSMIT_OFFSET: u64 = 0;
pub const SIMPLE_UART_STATUS_OFFSET: u64 = 1;
pub const SIMPLE_UART_SIZE: u64 = 2;
pub const SIMPLE_UART_SIZE: u64 = 1 << SIMPLE_UART_LOG2_SIZE;
pub const SIMPLE_UART_LOG2_SIZE: u8 = 1;
#[hdl(no_static)]
struct Operation<C: PhantomConstGet<CpuConfig> + PhantomConstCpuConfig> {
struct Operation<C: PhantomConstGet<MemoryInterfaceConfig>> {
start: MemoryOperationStart<C>,
finish: HdlOption<MemoryOperationFinish<C>>,
}
pub const fn simple_uart_memory_interface_config(op_id_width: usize) -> MemoryInterfaceConfig {
MemoryInterfaceConfig {
log2_bus_width_in_bytes: SIMPLE_UART_LOG2_SIZE,
queue_capacity: const { NonZeroUsize::new(1).unwrap() },
op_id_width,
}
}
#[hdl_module]
pub fn simple_uart(
config: PhantomConst<CpuConfig>,
config: PhantomConst<MemoryInterfaceConfig>,
clock_input_properties: PhantomConst<peripherals::ClockInputProperties>,
baud_rate: f64,
receiver_queue_size: NonZeroUsize,
) {
assert_eq!(
*config.get(),
simple_uart_memory_interface_config(config.get().op_id_width),
);
#[hdl]
let cd: ClockDomain = m.input();
#[hdl]
let memory_interface: MemoryInterface<PhantomConst<CpuConfig>> =
let memory_interface: MemoryInterface<PhantomConst<MemoryInterfaceConfig>> =
m.input(MemoryInterface[config]);
#[hdl]
let uart: peripherals::Uart = m.output();
@ -415,26 +427,23 @@ pub fn simple_uart(
.clock_domain(cd)
.reset(HdlOption[Operation[config]].HdlNone());
let next_fetch_block_ids_ty = memory_interface.ty().next_fetch_block_ids.HdlSome;
let next_op_ids_ty = memory_interface.ty().next_op_ids.HdlSome;
#[hdl]
if let HdlSome(operation) = operation_reg {
#[hdl]
let MemoryInterface::<_> {
start,
finish,
next_fetch_block_ids,
next_op_ids,
config: _,
} = memory_interface;
connect(start.ready, false);
connect(finish.data, operation.finish);
connect(
next_fetch_block_ids,
next_op_ids,
HdlSome(ArrayVec::from_parts_unchecked(
repeat(
operation.start.fetch_block_id,
next_fetch_block_ids_ty.capacity(),
),
1u8.cast_to(next_fetch_block_ids_ty.len_ty()),
repeat(operation.start.op_id, next_op_ids_ty.capacity()),
1u8.cast_to(next_op_ids_ty.len_ty()),
)),
);
connect(transmitter.input_byte.data, HdlNone());
@ -452,7 +461,7 @@ pub fn simple_uart(
addr,
write_data,
rw_mask,
fetch_block_id: _,
op_id: _,
config: _,
} = operation.start;
#[hdl]
@ -559,14 +568,14 @@ pub fn simple_uart(
let MemoryInterface::<_> {
start,
finish,
next_fetch_block_ids,
next_op_ids,
config: _,
} = memory_interface;
connect(start.ready, true);
connect(finish.data, finish.ty().data.HdlNone());
connect(
next_fetch_block_ids,
HdlSome(next_fetch_block_ids_ty.new_sim(next_fetch_block_ids_ty.element().zero())),
next_op_ids,
HdlSome(next_op_ids_ty.new_sim(next_op_ids_ty.element().zero())),
);
#[hdl]
if let HdlSome(start) = start.data {

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -5,8 +5,9 @@ use cpu::{
config::{CpuConfig, UnitConfig},
fetch::{FetchToDecodeInterface, FetchToDecodeInterfaceInner, fetch},
main_memory_and_io::{
MemoryInterface, MemoryOperationErrorKind, MemoryOperationFinish,
MemoryOperationFinishKind, MemoryOperationKind, MemoryOperationStart,
CpuConfigFetchMemoryInterfaceConfig, MemoryInterface, MemoryInterfaceConfig,
MemoryOperationErrorKind, MemoryOperationFinish, MemoryOperationFinishKind,
MemoryOperationKind, MemoryOperationStart,
},
next_pc::{FETCH_BLOCK_ID_WIDTH, NextPcToFetchInterface, NextPcToFetchInterfaceInner},
unit::UnitKind,
@ -92,8 +93,8 @@ fn mock_memory(config: PhantomConst<CpuConfig>) {
#[hdl]
let cd: ClockDomain = m.input();
#[hdl]
let memory_interface: MemoryInterface<PhantomConst<CpuConfig>> =
m.input(MemoryInterface[config]);
let memory_interface: MemoryInterface<PhantomConst<MemoryInterfaceConfig>> =
m.input(MemoryInterface[CpuConfigFetchMemoryInterfaceConfig[config]]);
#[hdl]
let queue_debug: ArrayVec<MemoryQueueEntry, ConstUsize<{ MEMORY_QUEUE_SIZE }>> = m.output();
m.register_clock_for_past(cd.clk);
@ -109,13 +110,12 @@ fn mock_memory(config: PhantomConst<CpuConfig>) {
let MemoryInterface::<_> {
start,
finish,
next_fetch_block_ids,
next_op_ids,
config: _,
} = memory_interface;
sim.write(start.ready, false).await;
sim.write(finish.data, finish.ty().data.HdlNone()).await;
sim.write(next_fetch_block_ids, next_fetch_block_ids.ty().HdlNone())
.await;
sim.write(next_op_ids, next_op_ids.ty().HdlNone()).await;
sim.write(
queue_debug,
queue_debug.ty().new_sim(MemoryQueueEntry.default_sim()),
@ -130,7 +130,7 @@ fn mock_memory(config: PhantomConst<CpuConfig>) {
#[hdl]
async fn run_fn(
cd: Expr<ClockDomain>,
memory_interface: Expr<MemoryInterface<PhantomConst<CpuConfig>>>,
memory_interface: Expr<MemoryInterface<PhantomConst<MemoryInterfaceConfig>>>,
queue_debug: Expr<ArrayVec<MemoryQueueEntry, ConstUsize<{ MEMORY_QUEUE_SIZE }>>>,
random: &RefCell<Random>,
mut sim: ExternModuleSimulationState,
@ -138,7 +138,7 @@ fn mock_memory(config: PhantomConst<CpuConfig>) {
let mut random = random.borrow_mut();
let config = memory_interface.config.ty();
let finish_data_ty = memory_interface.finish.data.ty();
let next_fetch_block_ids_ty = memory_interface.next_fetch_block_ids.ty();
let next_op_ids_ty = memory_interface.next_op_ids.ty();
let mut queue: VecDeque<SimValue<MemoryQueueEntry>> = VecDeque::new();
loop {
for entry in &mut queue {
@ -156,12 +156,17 @@ fn mock_memory(config: PhantomConst<CpuConfig>) {
)
.await;
sim.write(
memory_interface.next_fetch_block_ids,
memory_interface.next_op_ids,
#[hdl(sim)]
next_fetch_block_ids_ty.HdlSome(
next_fetch_block_ids_ty
next_op_ids_ty.HdlSome(
next_op_ids_ty
.HdlSome
.from_iter_sim(0u8, queue.iter().map(|entry| &entry.fetch_block_id))
.from_iter_sim(
0u8,
queue
.iter()
.map(|entry| SimValue::into_dyn_int(entry.fetch_block_id.clone())),
)
.ok()
.expect("queue is known to be small enough"),
),
@ -235,7 +240,7 @@ fn mock_memory(config: PhantomConst<CpuConfig>) {
addr,
write_data: _,
rw_mask,
fetch_block_id,
op_id,
config: _,
} = memory_operation_start;
#[hdl(sim)]
@ -250,7 +255,7 @@ fn mock_memory(config: PhantomConst<CpuConfig>) {
let entry = #[hdl(sim)]
MemoryQueueEntry {
addr,
fetch_block_id,
fetch_block_id: SimValue::from_dyn_int(op_id),
cycles_left: MemoryQueueEntry::get_next_delay(&mut random),
};
println!("mock memory start: {entry:#?}");

View file

@ -2,18 +2,16 @@
// See Notices.txt for copyright information
use cpu::{
config::{CpuConfig, UnitConfig},
main_memory_and_io::{
MemoryInterface, MemoryOperationErrorKind, MemoryOperationFinish,
MemoryInterface, MemoryInterfaceConfig, MemoryOperationErrorKind, MemoryOperationFinish,
MemoryOperationFinishKind, MemoryOperationKind, MemoryOperationStart,
simple_uart::{
ReceiverQueueStatus, SIMPLE_UART_RECEIVE_OFFSET, SIMPLE_UART_SIZE,
SIMPLE_UART_STATUS_OFFSET, SIMPLE_UART_TRANSMIT_OFFSET, receiver, receiver_no_queue,
simple_uart, transmitter, uart_clock_gen,
simple_uart, simple_uart_memory_interface_config, transmitter, uart_clock_gen,
},
},
next_pc::FETCH_BLOCK_ID_WIDTH,
unit::UnitKind,
util::array_vec::ArrayVec,
};
use fayalite::{
@ -579,7 +577,7 @@ fn test_receiver_and_uart_clock_gen() {
#[hdl_module]
fn simple_uart_loopback(
config: PhantomConst<CpuConfig>,
config: PhantomConst<MemoryInterfaceConfig>,
clock_input_properties: PhantomConst<peripherals::ClockInputProperties>,
baud_rate: f64,
receiver_queue_size: NonZeroUsize,
@ -587,7 +585,7 @@ fn simple_uart_loopback(
#[hdl]
let cd: ClockDomain = m.input();
#[hdl]
let memory_interface: MemoryInterface<PhantomConst<CpuConfig>> =
let memory_interface: MemoryInterface<PhantomConst<MemoryInterfaceConfig>> =
m.input(MemoryInterface[config]);
#[hdl]
let tx: Bool = m.output();
@ -604,15 +602,18 @@ fn simple_uart_loopback(
connect(inner.uart.rx, inner.uart.tx);
}
const SIMPLE_UART_MEMORY_INTERFACE_CONFIG: MemoryInterfaceConfig =
simple_uart_memory_interface_config(FETCH_BLOCK_ID_WIDTH);
struct MemoryOperationRunner<Sim: BorrowMut<Simulation<T>>, T: BundleType> {
sim: Sim,
clk: Expr<Clock>,
memory_interface: Expr<MemoryInterface<PhantomConst<CpuConfig>>>,
next_fetch_block_id: u64,
memory_interface: Expr<MemoryInterface<PhantomConst<MemoryInterfaceConfig>>>,
next_op_id: u64,
_phantom: PhantomData<T>,
}
const FETCH_WIDTH_IN_BYTES: usize = 1 << CpuConfig::DEFAULT_LOG2_FETCH_WIDTH_IN_BYTES;
const BUS_WIDTH_IN_BYTES: usize = SIMPLE_UART_MEMORY_INTERFACE_CONFIG.bus_width_in_bytes();
impl<Sim: BorrowMut<Simulation<T>>, T: BundleType> MemoryOperationRunner<Sim, T> {
#[hdl]
@ -620,24 +621,22 @@ impl<Sim: BorrowMut<Simulation<T>>, T: BundleType> MemoryOperationRunner<Sim, T>
&mut self,
kind: impl ToSimValue<Type = MemoryOperationKind>,
addr: u64,
write_data: [u8; FETCH_WIDTH_IN_BYTES],
rw_mask: [bool; FETCH_WIDTH_IN_BYTES],
write_data: [u8; BUS_WIDTH_IN_BYTES],
rw_mask: [bool; BUS_WIDTH_IN_BYTES],
timeout_cycles: usize,
) -> Result<[u8; FETCH_WIDTH_IN_BYTES], SimValue<MemoryOperationErrorKind>> {
) -> Result<[u8; BUS_WIDTH_IN_BYTES], SimValue<MemoryOperationErrorKind>> {
let kind = kind.into_sim_value();
assert_eq!(addr % FETCH_WIDTH_IN_BYTES as u64, 0);
assert_eq!(addr % BUS_WIDTH_IN_BYTES as u64, 0);
let config = self.memory_interface.ty().config;
let fetch_block_id = self
.next_fetch_block_id
.cast_to_static::<UInt<{ FETCH_BLOCK_ID_WIDTH }>>();
self.next_fetch_block_id += 1;
let op_id = self.next_op_id.cast_to(UInt[FETCH_BLOCK_ID_WIDTH]);
self.next_op_id += 1;
println!(
"started: MemoryOperationStart {{\n \
kind: {kind:?},\n \
addr: {addr:#x},\n \
write_data: {write_data:?},\n \
rw_mask: {rw_mask:?},\n \
fetch_block_id: {fetch_block_id},\n\
op_id: {op_id},\n\
}}"
);
let start_value = #[hdl(sim)]
@ -646,21 +645,21 @@ impl<Sim: BorrowMut<Simulation<T>>, T: BundleType> MemoryOperationRunner<Sim, T>
addr,
write_data: &write_data[..],
rw_mask: &rw_mask[..],
fetch_block_id,
op_id: op_id,
config,
};
#[hdl]
let MemoryInterface::<_> {
start,
finish,
next_fetch_block_ids,
next_op_ids,
config: _,
} = self.memory_interface;
let sim = self.sim.borrow_mut();
let check_io = |sim: &mut Simulation<T>,
expected_start_ready: bool,
expected_finish_data_is_some: bool,
expected_next_fetch_block_ids: &[_]| {
expected_next_op_ids: &[_]| {
assert_eq!(sim.read_bool(start.ready), expected_start_ready);
let finish_data = sim.read(finish.data);
let finish_data_is_some = #[hdl(sim)]
@ -675,15 +674,17 @@ impl<Sim: BorrowMut<Simulation<T>>, T: BundleType> MemoryOperationRunner<Sim, T>
);
#[hdl(sim)]
if let HdlSome(next_fetch_block_ids) = sim.read(next_fetch_block_ids) {
let next_fetch_block_ids: Vec<_> =
ArrayVec::elements_sim_ref(&next_fetch_block_ids)
.iter()
.map(|v| v.as_int())
.collect();
assert_eq!(next_fetch_block_ids, expected_next_fetch_block_ids);
if let HdlSome(next_op_ids) = sim.read(next_op_ids) {
let next_op_ids: Vec<_> = ArrayVec::elements_sim_ref(&next_op_ids)
.iter()
.map(|v| {
v.cast_to_static::<UInt<{ FETCH_BLOCK_ID_WIDTH }>>()
.as_int()
})
.collect();
assert_eq!(next_op_ids, expected_next_op_ids);
} else {
panic!("next_fetch_block_ids should be HdlSome");
panic!("next_op_ids should be HdlSome");
}
};
check_io(sim, true, false, &[]);
@ -702,7 +703,15 @@ impl<Sim: BorrowMut<Simulation<T>>, T: BundleType> MemoryOperationRunner<Sim, T>
#[hdl(sim)]
(start.ty().data).HdlNone(),
);
check_io(sim, false, false, &[start_value.fetch_block_id.as_int()]);
check_io(
sim,
false,
false,
&[start_value
.op_id
.cast_to_static::<UInt<{ FETCH_BLOCK_ID_WIDTH }>>()
.as_int()],
);
for _ in 0..timeout_cycles {
sim.advance_time(CLOCK_HALF_PERIOD);
sim.write_clock(self.clk, true);
@ -716,7 +725,7 @@ impl<Sim: BorrowMut<Simulation<T>>, T: BundleType> MemoryOperationRunner<Sim, T>
read_data,
config: _,
} = finish_data;
assert_eq!(read_data.len(), FETCH_WIDTH_IN_BYTES);
assert_eq!(read_data.len(), BUS_WIDTH_IN_BYTES);
let read_data = std::array::from_fn(|i| read_data[i].as_int());
println!(
"finished: MemoryOperationFinish {{\n \
@ -724,7 +733,15 @@ impl<Sim: BorrowMut<Simulation<T>>, T: BundleType> MemoryOperationRunner<Sim, T>
read_data: {read_data:?},\n\
}}"
);
check_io(sim, false, true, &[start_value.fetch_block_id.as_int()]);
check_io(
sim,
false,
true,
&[start_value
.op_id
.cast_to_static::<UInt<{ FETCH_BLOCK_ID_WIDTH }>>()
.as_int()],
);
sim.advance_time(CLOCK_HALF_PERIOD);
sim.write_clock(self.clk, true);
sim.advance_time(CLOCK_HALF_PERIOD);
@ -744,7 +761,15 @@ impl<Sim: BorrowMut<Simulation<T>>, T: BundleType> MemoryOperationRunner<Sim, T>
MemoryOperationFinishKind::Error(error) => Err(error),
};
}
check_io(sim, false, false, &[start_value.fetch_block_id.as_int()]);
check_io(
sim,
false,
false,
&[start_value
.op_id
.cast_to_static::<UInt<{ FETCH_BLOCK_ID_WIDTH }>>()
.as_int()],
);
}
panic!("memory operation took too long");
}
@ -756,16 +781,16 @@ impl<Sim: BorrowMut<Simulation<T>>, T: BundleType> MemoryOperationRunner<Sim, T>
bytes: &mut [u8],
timeout_cycles: usize,
) -> Result<(), SimValue<MemoryOperationErrorKind>> {
let offset_in_chunk = start_addr as usize % FETCH_WIDTH_IN_BYTES;
let offset_in_chunk = start_addr as usize % BUS_WIDTH_IN_BYTES;
let masked_addr = start_addr - offset_in_chunk as u64;
assert!(
offset_in_chunk
.checked_add(bytes.len())
.is_some_and(|v| v <= FETCH_WIDTH_IN_BYTES)
.is_some_and(|v| v <= BUS_WIDTH_IN_BYTES)
);
let bytes_range = offset_in_chunk..(offset_in_chunk + bytes.len());
let mut write_data = [0u8; FETCH_WIDTH_IN_BYTES];
let mut rw_mask = [false; FETCH_WIDTH_IN_BYTES];
let mut write_data = [0u8; BUS_WIDTH_IN_BYTES];
let mut rw_mask = [false; BUS_WIDTH_IN_BYTES];
rw_mask[bytes_range.clone()].fill(true);
if !is_read {
write_data[bytes_range.clone()].copy_from_slice(bytes);
@ -813,16 +838,9 @@ impl<Sim: BorrowMut<Simulation<T>>, T: BundleType> MemoryOperationRunner<Sim, T>
#[hdl]
fn test_simple_uart() {
let _n = SourceLocation::normalize_files_for_tests();
let config = CpuConfig::new(
vec![
UnitConfig::new(UnitKind::AluBranch),
UnitConfig::new(UnitKind::AluBranch),
],
NonZeroUsize::new(20).unwrap(),
);
const RECEIVER_QUEUE_SIZE: NonZeroUsize = NonZeroUsize::new(16).expect("not zero");
let m = simple_uart_loopback(
PhantomConst::new_sized(config),
PhantomConst::new_sized(SIMPLE_UART_MEMORY_INTERFACE_CONFIG),
clock_input_properties(),
BAUD_RATE,
RECEIVER_QUEUE_SIZE,
@ -859,7 +877,7 @@ fn test_simple_uart() {
let mut mem_op_runner = MemoryOperationRunner {
clk: sim.io().cd.clk,
memory_interface: sim.io().memory_interface,
next_fetch_block_id: 0,
next_op_id: 0,
_phantom: PhantomData,
sim,
};
@ -896,7 +914,7 @@ fn test_simple_uart() {
[0, empty_status],
);
for i in 0..2 * FETCH_WIDTH_IN_BYTES as u64 {
for i in 0..2 * BUS_WIDTH_IN_BYTES as u64 {
mem_op_runner
.read_bytes::<1>(SIMPLE_UART_SIZE + i, 1)
.unwrap_err();