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

View file

@ -1,17 +1,53 @@
// SPDX-License-Identifier: LGPL-3.0-or-later // SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information // See Notices.txt for copyright information
use crate::{ use crate::{config::CpuConfig, next_pc::FETCH_BLOCK_ID_WIDTH, util::array_vec::ArrayVec};
config::{
CpuConfig, CpuConfigFetchWidthInBytes, CpuConfigMaxFetchesInFlight, PhantomConstCpuConfig,
},
next_pc::FETCH_BLOCK_ID_WIDTH,
util::array_vec::ArrayVec,
};
use fayalite::{prelude::*, util::ready_valid::ReadyValid}; use fayalite::{prelude::*, util::ready_valid::ReadyValid};
use std::num::NonZeroUsize;
pub mod simple_uart; 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] #[hdl]
pub enum MemoryOperationKind { pub enum MemoryOperationKind {
Read, Read,
@ -19,12 +55,12 @@ pub enum MemoryOperationKind {
} }
#[hdl(no_static)] #[hdl(no_static)]
pub struct MemoryOperationStart<C: PhantomConstGet<CpuConfig> + PhantomConstCpuConfig> { pub struct MemoryOperationStart<C: PhantomConstGet<MemoryInterfaceConfig>> {
pub kind: MemoryOperationKind, pub kind: MemoryOperationKind,
pub addr: UInt<64>, pub addr: UInt<64>,
pub write_data: ArrayType<UInt<8>, CpuConfigFetchWidthInBytes<C>>, pub write_data: ArrayType<UInt<8>, MemoryInterfaceBusWidthInBytes<C>>,
pub rw_mask: ArrayType<Bool, CpuConfigFetchWidthInBytes<C>>, pub rw_mask: ArrayType<Bool, MemoryInterfaceBusWidthInBytes<C>>,
pub fetch_block_id: UInt<{ FETCH_BLOCK_ID_WIDTH }>, // for debugging pub op_id: MemoryInterfaceOpId<C>, // for debugging
pub config: C, pub config: C,
} }
@ -40,20 +76,19 @@ pub enum MemoryOperationFinishKind {
} }
#[hdl(no_static)] #[hdl(no_static)]
pub struct MemoryOperationFinish<C: PhantomConstGet<CpuConfig> + PhantomConstCpuConfig> { pub struct MemoryOperationFinish<C: PhantomConstGet<MemoryInterfaceConfig>> {
pub kind: MemoryOperationFinishKind, pub kind: MemoryOperationFinishKind,
pub read_data: ArrayType<UInt<8>, CpuConfigFetchWidthInBytes<C>>, pub read_data: ArrayType<UInt<8>, MemoryInterfaceBusWidthInBytes<C>>,
pub config: C, pub config: C,
} }
#[hdl(no_static)] #[hdl(no_static)]
pub struct MemoryInterface<C: PhantomConstGet<CpuConfig> + PhantomConstCpuConfig> { pub struct MemoryInterface<C: PhantomConstGet<MemoryInterfaceConfig>> {
pub start: ReadyValid<MemoryOperationStart<C>>, pub start: ReadyValid<MemoryOperationStart<C>>,
#[hdl(flip)] #[hdl(flip)]
pub finish: ReadyValid<MemoryOperationFinish<C>>, pub finish: ReadyValid<MemoryOperationFinish<C>>,
/// for debugging /// for debugging
#[hdl(flip)] #[hdl(flip)]
pub next_fetch_block_ids: pub next_op_ids: HdlOption<ArrayVec<MemoryInterfaceOpId<C>, MemoryInterfaceQueueCapacity<C>>>,
HdlOption<ArrayVec<UInt<{ FETCH_BLOCK_ID_WIDTH }>, CpuConfigMaxFetchesInFlight<C>>>,
pub config: C, pub config: C,
} }

View file

@ -4,9 +4,8 @@
use std::num::NonZeroUsize; use std::num::NonZeroUsize;
use crate::{ use crate::{
config::{CpuConfig, PhantomConstCpuConfig},
main_memory_and_io::{ main_memory_and_io::{
MemoryInterface, MemoryOperationErrorKind, MemoryOperationFinish, MemoryInterface, MemoryInterfaceConfig, MemoryOperationErrorKind, MemoryOperationFinish,
MemoryOperationFinishKind, MemoryOperationKind, MemoryOperationStart, MemoryOperationFinishKind, MemoryOperationKind, MemoryOperationStart,
}, },
util::array_vec::ArrayVec, 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_RECEIVE_OFFSET: u64 = 0;
pub const SIMPLE_UART_TRANSMIT_OFFSET: u64 = 0; pub const SIMPLE_UART_TRANSMIT_OFFSET: u64 = 0;
pub const SIMPLE_UART_STATUS_OFFSET: u64 = 1; 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)] #[hdl(no_static)]
struct Operation<C: PhantomConstGet<CpuConfig> + PhantomConstCpuConfig> { struct Operation<C: PhantomConstGet<MemoryInterfaceConfig>> {
start: MemoryOperationStart<C>, start: MemoryOperationStart<C>,
finish: HdlOption<MemoryOperationFinish<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] #[hdl_module]
pub fn simple_uart( pub fn simple_uart(
config: PhantomConst<CpuConfig>, config: PhantomConst<MemoryInterfaceConfig>,
clock_input_properties: PhantomConst<peripherals::ClockInputProperties>, clock_input_properties: PhantomConst<peripherals::ClockInputProperties>,
baud_rate: f64, baud_rate: f64,
receiver_queue_size: NonZeroUsize, receiver_queue_size: NonZeroUsize,
) { ) {
assert_eq!(
*config.get(),
simple_uart_memory_interface_config(config.get().op_id_width),
);
#[hdl] #[hdl]
let cd: ClockDomain = m.input(); let cd: ClockDomain = m.input();
#[hdl] #[hdl]
let memory_interface: MemoryInterface<PhantomConst<CpuConfig>> = let memory_interface: MemoryInterface<PhantomConst<MemoryInterfaceConfig>> =
m.input(MemoryInterface[config]); m.input(MemoryInterface[config]);
#[hdl] #[hdl]
let uart: peripherals::Uart = m.output(); let uart: peripherals::Uart = m.output();
@ -415,26 +427,23 @@ pub fn simple_uart(
.clock_domain(cd) .clock_domain(cd)
.reset(HdlOption[Operation[config]].HdlNone()); .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] #[hdl]
if let HdlSome(operation) = operation_reg { if let HdlSome(operation) = operation_reg {
#[hdl] #[hdl]
let MemoryInterface::<_> { let MemoryInterface::<_> {
start, start,
finish, finish,
next_fetch_block_ids, next_op_ids,
config: _, config: _,
} = memory_interface; } = memory_interface;
connect(start.ready, false); connect(start.ready, false);
connect(finish.data, operation.finish); connect(finish.data, operation.finish);
connect( connect(
next_fetch_block_ids, next_op_ids,
HdlSome(ArrayVec::from_parts_unchecked( HdlSome(ArrayVec::from_parts_unchecked(
repeat( repeat(operation.start.op_id, next_op_ids_ty.capacity()),
operation.start.fetch_block_id, 1u8.cast_to(next_op_ids_ty.len_ty()),
next_fetch_block_ids_ty.capacity(),
),
1u8.cast_to(next_fetch_block_ids_ty.len_ty()),
)), )),
); );
connect(transmitter.input_byte.data, HdlNone()); connect(transmitter.input_byte.data, HdlNone());
@ -452,7 +461,7 @@ pub fn simple_uart(
addr, addr,
write_data, write_data,
rw_mask, rw_mask,
fetch_block_id: _, op_id: _,
config: _, config: _,
} = operation.start; } = operation.start;
#[hdl] #[hdl]
@ -559,14 +568,14 @@ pub fn simple_uart(
let MemoryInterface::<_> { let MemoryInterface::<_> {
start, start,
finish, finish,
next_fetch_block_ids, next_op_ids,
config: _, config: _,
} = memory_interface; } = memory_interface;
connect(start.ready, true); connect(start.ready, true);
connect(finish.data, finish.ty().data.HdlNone()); connect(finish.data, finish.ty().data.HdlNone());
connect( connect(
next_fetch_block_ids, next_op_ids,
HdlSome(next_fetch_block_ids_ty.new_sim(next_fetch_block_ids_ty.element().zero())), HdlSome(next_op_ids_ty.new_sim(next_op_ids_ty.element().zero())),
); );
#[hdl] #[hdl]
if let HdlSome(start) = start.data { 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}, config::{CpuConfig, UnitConfig},
fetch::{FetchToDecodeInterface, FetchToDecodeInterfaceInner, fetch}, fetch::{FetchToDecodeInterface, FetchToDecodeInterfaceInner, fetch},
main_memory_and_io::{ main_memory_and_io::{
MemoryInterface, MemoryOperationErrorKind, MemoryOperationFinish, CpuConfigFetchMemoryInterfaceConfig, MemoryInterface, MemoryInterfaceConfig,
MemoryOperationFinishKind, MemoryOperationKind, MemoryOperationStart, MemoryOperationErrorKind, MemoryOperationFinish, MemoryOperationFinishKind,
MemoryOperationKind, MemoryOperationStart,
}, },
next_pc::{FETCH_BLOCK_ID_WIDTH, NextPcToFetchInterface, NextPcToFetchInterfaceInner}, next_pc::{FETCH_BLOCK_ID_WIDTH, NextPcToFetchInterface, NextPcToFetchInterfaceInner},
unit::UnitKind, unit::UnitKind,
@ -92,8 +93,8 @@ fn mock_memory(config: PhantomConst<CpuConfig>) {
#[hdl] #[hdl]
let cd: ClockDomain = m.input(); let cd: ClockDomain = m.input();
#[hdl] #[hdl]
let memory_interface: MemoryInterface<PhantomConst<CpuConfig>> = let memory_interface: MemoryInterface<PhantomConst<MemoryInterfaceConfig>> =
m.input(MemoryInterface[config]); m.input(MemoryInterface[CpuConfigFetchMemoryInterfaceConfig[config]]);
#[hdl] #[hdl]
let queue_debug: ArrayVec<MemoryQueueEntry, ConstUsize<{ MEMORY_QUEUE_SIZE }>> = m.output(); let queue_debug: ArrayVec<MemoryQueueEntry, ConstUsize<{ MEMORY_QUEUE_SIZE }>> = m.output();
m.register_clock_for_past(cd.clk); m.register_clock_for_past(cd.clk);
@ -109,13 +110,12 @@ fn mock_memory(config: PhantomConst<CpuConfig>) {
let MemoryInterface::<_> { let MemoryInterface::<_> {
start, start,
finish, finish,
next_fetch_block_ids, next_op_ids,
config: _, config: _,
} = memory_interface; } = memory_interface;
sim.write(start.ready, false).await; sim.write(start.ready, false).await;
sim.write(finish.data, finish.ty().data.HdlNone()).await; sim.write(finish.data, finish.ty().data.HdlNone()).await;
sim.write(next_fetch_block_ids, next_fetch_block_ids.ty().HdlNone()) sim.write(next_op_ids, next_op_ids.ty().HdlNone()).await;
.await;
sim.write( sim.write(
queue_debug, queue_debug,
queue_debug.ty().new_sim(MemoryQueueEntry.default_sim()), queue_debug.ty().new_sim(MemoryQueueEntry.default_sim()),
@ -130,7 +130,7 @@ fn mock_memory(config: PhantomConst<CpuConfig>) {
#[hdl] #[hdl]
async fn run_fn( async fn run_fn(
cd: Expr<ClockDomain>, cd: Expr<ClockDomain>,
memory_interface: Expr<MemoryInterface<PhantomConst<CpuConfig>>>, memory_interface: Expr<MemoryInterface<PhantomConst<MemoryInterfaceConfig>>>,
queue_debug: Expr<ArrayVec<MemoryQueueEntry, ConstUsize<{ MEMORY_QUEUE_SIZE }>>>, queue_debug: Expr<ArrayVec<MemoryQueueEntry, ConstUsize<{ MEMORY_QUEUE_SIZE }>>>,
random: &RefCell<Random>, random: &RefCell<Random>,
mut sim: ExternModuleSimulationState, mut sim: ExternModuleSimulationState,
@ -138,7 +138,7 @@ fn mock_memory(config: PhantomConst<CpuConfig>) {
let mut random = random.borrow_mut(); let mut random = random.borrow_mut();
let config = memory_interface.config.ty(); let config = memory_interface.config.ty();
let finish_data_ty = memory_interface.finish.data.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(); let mut queue: VecDeque<SimValue<MemoryQueueEntry>> = VecDeque::new();
loop { loop {
for entry in &mut queue { for entry in &mut queue {
@ -156,12 +156,17 @@ fn mock_memory(config: PhantomConst<CpuConfig>) {
) )
.await; .await;
sim.write( sim.write(
memory_interface.next_fetch_block_ids, memory_interface.next_op_ids,
#[hdl(sim)] #[hdl(sim)]
next_fetch_block_ids_ty.HdlSome( next_op_ids_ty.HdlSome(
next_fetch_block_ids_ty next_op_ids_ty
.HdlSome .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() .ok()
.expect("queue is known to be small enough"), .expect("queue is known to be small enough"),
), ),
@ -235,7 +240,7 @@ fn mock_memory(config: PhantomConst<CpuConfig>) {
addr, addr,
write_data: _, write_data: _,
rw_mask, rw_mask,
fetch_block_id, op_id,
config: _, config: _,
} = memory_operation_start; } = memory_operation_start;
#[hdl(sim)] #[hdl(sim)]
@ -250,7 +255,7 @@ fn mock_memory(config: PhantomConst<CpuConfig>) {
let entry = #[hdl(sim)] let entry = #[hdl(sim)]
MemoryQueueEntry { MemoryQueueEntry {
addr, addr,
fetch_block_id, fetch_block_id: SimValue::from_dyn_int(op_id),
cycles_left: MemoryQueueEntry::get_next_delay(&mut random), cycles_left: MemoryQueueEntry::get_next_delay(&mut random),
}; };
println!("mock memory start: {entry:#?}"); println!("mock memory start: {entry:#?}");

View file

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