forked from libre-chip/cpu
WIP adding memory_interface_adaptor
This commit is contained in:
parent
3080ea4ce2
commit
b09fc821fe
1 changed files with 566 additions and 2 deletions
|
|
@ -2,8 +2,18 @@
|
||||||
// See Notices.txt for copyright information
|
// See Notices.txt for copyright information
|
||||||
|
|
||||||
use crate::{config::CpuConfig, 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 fayalite::{
|
||||||
use std::num::{NonZeroU64, NonZeroUsize, Wrapping};
|
bundle::BundleType,
|
||||||
|
expr::Valueless,
|
||||||
|
int::{UIntInRangeInclusiveType, UIntInRangeType},
|
||||||
|
intern::{Intern, Interned},
|
||||||
|
prelude::*,
|
||||||
|
util::ready_valid::{ReadyValid, queue},
|
||||||
|
};
|
||||||
|
use std::{
|
||||||
|
fmt,
|
||||||
|
num::{NonZeroU64, NonZeroUsize, Wrapping},
|
||||||
|
};
|
||||||
|
|
||||||
pub mod simple_uart;
|
pub mod simple_uart;
|
||||||
|
|
||||||
|
|
@ -187,3 +197,557 @@ pub struct MemoryInterface<C: PhantomConstGet<MemoryInterfaceConfig>> {
|
||||||
pub next_op_ids: HdlOption<ArrayVec<MemoryInterfaceOpId<C>, MemoryInterfaceQueueCapacity<C>>>,
|
pub next_op_ids: HdlOption<ArrayVec<MemoryInterfaceOpId<C>, MemoryInterfaceQueueCapacity<C>>>,
|
||||||
pub config: C,
|
pub config: C,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const fn memory_interface_always_error_config(
|
||||||
|
base_config: MemoryInterfaceConfig,
|
||||||
|
) -> MemoryInterfaceConfig {
|
||||||
|
let MemoryInterfaceConfig {
|
||||||
|
log2_bus_width_in_bytes,
|
||||||
|
queue_capacity: _,
|
||||||
|
op_id_width,
|
||||||
|
address_range: _,
|
||||||
|
} = base_config;
|
||||||
|
MemoryInterfaceConfig {
|
||||||
|
log2_bus_width_in_bytes,
|
||||||
|
queue_capacity: const { NonZeroUsize::new(1).unwrap() },
|
||||||
|
op_id_width,
|
||||||
|
address_range: AddressRange::Full,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl_module]
|
||||||
|
pub fn memory_interface_always_error(config: PhantomConst<MemoryInterfaceConfig>) {
|
||||||
|
assert_eq!(
|
||||||
|
*config.get(),
|
||||||
|
memory_interface_always_error_config(*config.get()),
|
||||||
|
);
|
||||||
|
#[hdl]
|
||||||
|
let input_interface: MemoryInterface<PhantomConst<MemoryInterfaceConfig>> =
|
||||||
|
m.input(MemoryInterface[config]);
|
||||||
|
|
||||||
|
connect(
|
||||||
|
input_interface.next_op_ids,
|
||||||
|
input_interface.ty().next_op_ids.HdlNone(),
|
||||||
|
);
|
||||||
|
connect(input_interface.start.ready, input_interface.finish.ready);
|
||||||
|
connect(
|
||||||
|
input_interface.finish.data,
|
||||||
|
input_interface.ty().finish.data.HdlNone(),
|
||||||
|
);
|
||||||
|
#[hdl]
|
||||||
|
if let HdlSome(_) = input_interface.start.data {
|
||||||
|
connect(
|
||||||
|
input_interface.finish.data,
|
||||||
|
HdlSome(
|
||||||
|
#[hdl]
|
||||||
|
MemoryOperationFinish::<_> {
|
||||||
|
kind: MemoryOperationFinishKind.Error(MemoryOperationErrorKind.Generic()),
|
||||||
|
read_data: repeat(0u8, MemoryInterfaceBusWidthInBytes[config]),
|
||||||
|
config,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct MemoryInterfacesBundleFieldPath(pub Interned<[Interned<str>]>);
|
||||||
|
|
||||||
|
impl MemoryInterfacesBundleFieldPath {
|
||||||
|
pub fn from_slice(path: &[Interned<str>]) -> Self {
|
||||||
|
Self(path.intern())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for MemoryInterfacesBundleFieldPath {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
if self.0.is_empty() {
|
||||||
|
return f.write_str("<empty path>");
|
||||||
|
}
|
||||||
|
for (i, name) in self.0.iter().enumerate() {
|
||||||
|
if i != 0 {
|
||||||
|
f.write_str(".")?;
|
||||||
|
}
|
||||||
|
if name.is_empty() || name.contains(|ch: char| !ch.is_ascii_alphanumeric() && ch != '_')
|
||||||
|
{
|
||||||
|
write!(f, "{name:?}")?;
|
||||||
|
} else {
|
||||||
|
f.write_str(name)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for MemoryInterfacesBundleFieldPath {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
fmt::Display::fmt(self, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct MemoryInterfacesBundleField<
|
||||||
|
T: ValueType<Type = MemoryInterface<PhantomConst<MemoryInterfaceConfig>>>,
|
||||||
|
> {
|
||||||
|
pub path: MemoryInterfacesBundleFieldPath,
|
||||||
|
pub value: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct MemoryInterfacesBundleProperties<
|
||||||
|
T: ValueType<Type = MemoryInterface<PhantomConst<MemoryInterfaceConfig>>>,
|
||||||
|
> {
|
||||||
|
first_full_interface: Option<usize>,
|
||||||
|
fields: Vec<MemoryInterfacesBundleField<T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ValueType<Type = MemoryInterface<PhantomConst<MemoryInterfaceConfig>>>>
|
||||||
|
MemoryInterfacesBundleProperties<T>
|
||||||
|
{
|
||||||
|
pub fn first_full_interface(&self) -> Option<usize> {
|
||||||
|
self.first_full_interface
|
||||||
|
}
|
||||||
|
pub fn fields(&self) -> &[MemoryInterfacesBundleField<T>] {
|
||||||
|
&self.fields
|
||||||
|
}
|
||||||
|
pub fn into_fields(self) -> Vec<MemoryInterfacesBundleField<T>> {
|
||||||
|
self.fields
|
||||||
|
}
|
||||||
|
#[track_caller]
|
||||||
|
pub fn from_fields(fields: Vec<MemoryInterfacesBundleField<T>>) -> Self {
|
||||||
|
let mut first_full_interface = None;
|
||||||
|
for (index, field) in fields.iter().enumerate() {
|
||||||
|
let ty = field.value.ty();
|
||||||
|
assert_eq!(
|
||||||
|
ty, MemoryInterface[ty.config],
|
||||||
|
"inconsistent field type: {}",
|
||||||
|
field.path,
|
||||||
|
);
|
||||||
|
if let None = first_full_interface
|
||||||
|
&& let AddressRange::Full = ty.config.get().address_range
|
||||||
|
{
|
||||||
|
first_full_interface = Some(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self {
|
||||||
|
first_full_interface,
|
||||||
|
fields,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[track_caller]
|
||||||
|
fn get_fields<
|
||||||
|
B: ValueType<Type = Bundle, ValueCategory = T::ValueCategory>,
|
||||||
|
C: ValueType<Type = CanonicalType, ValueCategory = T::ValueCategory>,
|
||||||
|
>(
|
||||||
|
fields: &mut Vec<MemoryInterfacesBundleField<T>>,
|
||||||
|
path_prefix: &mut Vec<Interned<str>>,
|
||||||
|
bundle: B,
|
||||||
|
get_field: impl Copy + Fn(&B, Interned<str>) -> C,
|
||||||
|
interface_from_bundle: impl Copy + Fn(B) -> T,
|
||||||
|
bundle_from_canonical: impl Copy + Fn(C) -> B,
|
||||||
|
) {
|
||||||
|
let bundle_fields = bundle.ty().fields();
|
||||||
|
if bundle_fields.iter().any(|f| f.flipped)
|
||||||
|
&& bundle_fields.iter().any(|f| *f.name == *"config")
|
||||||
|
{
|
||||||
|
let value = interface_from_bundle(bundle);
|
||||||
|
fields.push(MemoryInterfacesBundleField {
|
||||||
|
path: MemoryInterfacesBundleFieldPath::from_slice(path_prefix),
|
||||||
|
value,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
for f in &bundle_fields {
|
||||||
|
assert!(
|
||||||
|
!f.flipped,
|
||||||
|
"field must not have #[hdl(flip)]: {}",
|
||||||
|
MemoryInterfacesBundleFieldPath::from_slice(path_prefix),
|
||||||
|
);
|
||||||
|
let field = get_field(&bundle, f.name);
|
||||||
|
match field.ty() {
|
||||||
|
CanonicalType::Bundle(_) => {
|
||||||
|
path_prefix.push(f.name);
|
||||||
|
Self::get_fields(
|
||||||
|
fields,
|
||||||
|
path_prefix,
|
||||||
|
bundle_from_canonical(field),
|
||||||
|
get_field,
|
||||||
|
interface_from_bundle,
|
||||||
|
bundle_from_canonical,
|
||||||
|
);
|
||||||
|
path_prefix.pop();
|
||||||
|
}
|
||||||
|
CanonicalType::PhantomConst(_) => continue,
|
||||||
|
_ => panic!(
|
||||||
|
"field type must be either a MemoryInterfacesBundle or a PhantomConst: {}",
|
||||||
|
MemoryInterfacesBundleFieldPath::from_slice(path_prefix),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Self` is a bundle where either:
|
||||||
|
/// * `Self` is a [`MemoryInterface<PhantomConst<MemoryInterfaceConfig>>`]
|
||||||
|
/// * each field is a [`MemoryInterfacesBundle`] or a [`PhantomConst`] and none of the fields have `#[hdl(flip)]`
|
||||||
|
pub trait MemoryInterfacesBundle: BundleType {
|
||||||
|
#[track_caller]
|
||||||
|
fn properties_valueless(
|
||||||
|
self,
|
||||||
|
mut path_prefix: Vec<Interned<str>>,
|
||||||
|
) -> MemoryInterfacesBundleProperties<
|
||||||
|
Valueless<MemoryInterface<PhantomConst<MemoryInterfaceConfig>>>,
|
||||||
|
> {
|
||||||
|
let mut fields = Vec::new();
|
||||||
|
MemoryInterfacesBundleProperties::get_fields(
|
||||||
|
&mut fields,
|
||||||
|
&mut path_prefix,
|
||||||
|
Valueless::new(Bundle::new(self.fields())),
|
||||||
|
|&bundle, name| {
|
||||||
|
Valueless::new(
|
||||||
|
bundle
|
||||||
|
.ty()
|
||||||
|
.field_by_name(name)
|
||||||
|
.expect("field is known to exist")
|
||||||
|
.ty,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|bundle| Valueless::new(MemoryInterface::from_canonical(bundle.ty().canonical())),
|
||||||
|
|canonical| Valueless::new(Bundle::from_canonical(canonical.ty())),
|
||||||
|
);
|
||||||
|
MemoryInterfacesBundleProperties::from_fields(fields)
|
||||||
|
}
|
||||||
|
#[track_caller]
|
||||||
|
fn properties_expr(
|
||||||
|
this: impl ToExpr<Type = Self>,
|
||||||
|
mut path_prefix: Vec<Interned<str>>,
|
||||||
|
) -> MemoryInterfacesBundleProperties<Expr<MemoryInterface<PhantomConst<MemoryInterfaceConfig>>>>
|
||||||
|
{
|
||||||
|
let mut fields = Vec::new();
|
||||||
|
MemoryInterfacesBundleProperties::get_fields(
|
||||||
|
&mut fields,
|
||||||
|
&mut path_prefix,
|
||||||
|
Expr::as_bundle(this.to_expr()),
|
||||||
|
|&bundle, name| Expr::field(bundle, &name),
|
||||||
|
Expr::from_bundle,
|
||||||
|
Expr::from_canonical,
|
||||||
|
);
|
||||||
|
MemoryInterfacesBundleProperties::from_fields(fields)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn memory_interface_resize_adaptor_input_config(
|
||||||
|
output_config: PhantomConst<MemoryInterfaceConfig>,
|
||||||
|
input_log2_bus_width_in_bytes: u8,
|
||||||
|
max_input_queue_capacity: Option<NonZeroUsize>,
|
||||||
|
) -> PhantomConst<MemoryInterfaceConfig> {
|
||||||
|
let output_config = *output_config.get();
|
||||||
|
let MemoryInterfaceConfig {
|
||||||
|
log2_bus_width_in_bytes: _,
|
||||||
|
queue_capacity: output_queue_capacity,
|
||||||
|
op_id_width,
|
||||||
|
address_range,
|
||||||
|
} = output_config;
|
||||||
|
let mut input_config = MemoryInterfaceConfig {
|
||||||
|
log2_bus_width_in_bytes: input_log2_bus_width_in_bytes,
|
||||||
|
queue_capacity: output_queue_capacity,
|
||||||
|
op_id_width,
|
||||||
|
address_range,
|
||||||
|
};
|
||||||
|
if let Some(ratio) =
|
||||||
|
NonZeroUsize::new(input_config.bus_width_in_bytes() / output_config.bus_width_in_bytes())
|
||||||
|
{
|
||||||
|
input_config.queue_capacity = output_queue_capacity.div_ceil(ratio)
|
||||||
|
}
|
||||||
|
if let Some(max_input_queue_capacity) = max_input_queue_capacity
|
||||||
|
&& max_input_queue_capacity < input_config.queue_capacity
|
||||||
|
{
|
||||||
|
input_config.queue_capacity = max_input_queue_capacity;
|
||||||
|
}
|
||||||
|
PhantomConst::new_sized(input_config)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl_module]
|
||||||
|
fn memory_interface_resize_adaptor(
|
||||||
|
input_config: PhantomConst<MemoryInterfaceConfig>,
|
||||||
|
output_config: PhantomConst<MemoryInterfaceConfig>,
|
||||||
|
) {
|
||||||
|
let expected_input_config = memory_interface_resize_adaptor_input_config(
|
||||||
|
output_config,
|
||||||
|
input_config.get().log2_bus_width_in_bytes,
|
||||||
|
Some(input_config.get().queue_capacity),
|
||||||
|
);
|
||||||
|
assert_eq!(input_config, expected_input_config);
|
||||||
|
#[hdl]
|
||||||
|
let cd: ClockDomain = m.input();
|
||||||
|
#[hdl]
|
||||||
|
let input_interface: MemoryInterface<PhantomConst<MemoryInterfaceConfig>> =
|
||||||
|
m.input(MemoryInterface[input_config]);
|
||||||
|
#[hdl]
|
||||||
|
let output_interface: MemoryInterface<PhantomConst<MemoryInterfaceConfig>> =
|
||||||
|
m.output(MemoryInterface[output_config]);
|
||||||
|
|
||||||
|
let log2_split_count = output_config
|
||||||
|
.get()
|
||||||
|
.log2_bus_width_in_bytes
|
||||||
|
.saturating_sub(input_config.get().log2_bus_width_in_bytes);
|
||||||
|
let split_count = 1usize.strict_shl(log2_split_count.into());
|
||||||
|
|
||||||
|
connect(
|
||||||
|
input_interface.next_op_ids,
|
||||||
|
input_interface.ty().next_op_ids.HdlNone(),
|
||||||
|
);
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
let input_start_active_split_chunks = wire(Array[Bool][split_count]);
|
||||||
|
|
||||||
|
// default to [true, false, false, false, ...]
|
||||||
|
connect(input_start_active_split_chunks, repeat(false, split_count));
|
||||||
|
connect(input_start_active_split_chunks[0], true);
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
if let HdlSome(start) = input_interface.start.data {
|
||||||
|
#[hdl]
|
||||||
|
let MemoryOperationStart::<_> {
|
||||||
|
kind,
|
||||||
|
addr,
|
||||||
|
write_data,
|
||||||
|
rw_mask,
|
||||||
|
op_id,
|
||||||
|
config,
|
||||||
|
} = start;
|
||||||
|
// if rw_mask is all false, leave input_start_active_split_chunks as [true, false, false, false, ...] so we make at least one transaction on output_interface
|
||||||
|
#[hdl]
|
||||||
|
if rw_mask.cast_to_bits().any_one_bits() {
|
||||||
|
// otherwise, split rw_mask into chunks of size at most output_config.get().bus_width_in_bytes()
|
||||||
|
for (split_chunk, rw_mask_chunk) in input_start_active_split_chunks
|
||||||
|
.into_iter()
|
||||||
|
.zip(rw_mask.chunks(output_config.get().bus_width_in_bytes()))
|
||||||
|
{
|
||||||
|
connect(split_chunk, rw_mask_chunk.cast_to_bits().any_one_bits());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl(no_static)]
|
||||||
|
struct Entry<C: PhantomConstGet<MemoryInterfaceConfig>, SplitCount: Size> {
|
||||||
|
active_split_chunks: ArrayType<Bool, SplitCount>,
|
||||||
|
config: C,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
let queue = instance(queue(
|
||||||
|
Entry[input_config][split_count],
|
||||||
|
input_config.get().queue_capacity,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
));
|
||||||
|
|
||||||
|
connect(queue.cd, cd);
|
||||||
|
|
||||||
|
#[hdl(no_static)]
|
||||||
|
struct IncompleteStartState<C: PhantomConstGet<MemoryInterfaceConfig>, SplitCount: Size> {
|
||||||
|
start: MemoryOperationStart<C>,
|
||||||
|
split_chunks_left: ArrayType<Bool, SplitCount>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
let incomplete_start_state_reg = reg_builder()
|
||||||
|
.clock_domain(cd)
|
||||||
|
.reset(HdlOption[IncompleteStartState[input_config][split_count]].HdlNone());
|
||||||
|
|
||||||
|
connect(
|
||||||
|
input_interface.start.ready,
|
||||||
|
HdlOption::is_none(incomplete_start_state_reg) & queue.inp.ready,
|
||||||
|
);
|
||||||
|
|
||||||
|
#[hdl(no_static)]
|
||||||
|
struct IncompleteFinishState<C: PhantomConstGet<MemoryInterfaceConfig>, SplitCount: Size> {
|
||||||
|
start: MemoryOperationFinish<C>,
|
||||||
|
split_chunks_left: ArrayType<Bool, SplitCount>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
let incomplete_finish_state_reg = reg_builder()
|
||||||
|
.clock_domain(cd)
|
||||||
|
.reset(HdlOption[IncompleteFinishState[input_config][split_count]].HdlNone());
|
||||||
|
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl_module]
|
||||||
|
pub fn memory_interface_adaptor<OutputInterfaces: Type + MemoryInterfacesBundle>(
|
||||||
|
input_interface_config: PhantomConst<MemoryInterfaceConfig>,
|
||||||
|
output_interfaces_ty: OutputInterfaces,
|
||||||
|
) {
|
||||||
|
#[hdl]
|
||||||
|
let cd: ClockDomain = m.input();
|
||||||
|
#[hdl]
|
||||||
|
let input_interface: MemoryInterface<PhantomConst<MemoryInterfaceConfig>> =
|
||||||
|
m.input(MemoryInterface[input_interface_config]);
|
||||||
|
#[hdl]
|
||||||
|
let output_interfaces: OutputInterfaces = m.output(output_interfaces_ty);
|
||||||
|
let mut output_interfaces = MemoryInterfacesBundle::properties_expr(
|
||||||
|
output_interfaces,
|
||||||
|
vec!["output_interfaces".intern()],
|
||||||
|
);
|
||||||
|
if let Some(index) = output_interfaces.first_full_interface() {
|
||||||
|
assert!(
|
||||||
|
index == output_interfaces.fields().len() - 1,
|
||||||
|
"all fields after {path} will never be used since {path} comes before and handles all addresses",
|
||||||
|
path = output_interfaces.fields[index].path,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
#[hdl]
|
||||||
|
let always_error = instance(memory_interface_always_error(PhantomConst::new_sized(
|
||||||
|
memory_interface_always_error_config(*input_interface_config.get()),
|
||||||
|
)));
|
||||||
|
let mut fields = output_interfaces.into_fields();
|
||||||
|
fields.push(MemoryInterfacesBundleField {
|
||||||
|
path: MemoryInterfacesBundleFieldPath::from_slice(&[
|
||||||
|
"always_error".intern(),
|
||||||
|
"input_interface".intern(),
|
||||||
|
]),
|
||||||
|
value: always_error.input_interface,
|
||||||
|
});
|
||||||
|
output_interfaces = MemoryInterfacesBundleProperties::from_fields(fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_selected_output_interface(
|
||||||
|
output_interfaces: &MemoryInterfacesBundleProperties<
|
||||||
|
Expr<MemoryInterface<PhantomConst<MemoryInterfaceConfig>>>,
|
||||||
|
>,
|
||||||
|
selected_output_interface: impl ToExpr<Type = UIntInRangeType<ConstUsize<0>, DynSize>>,
|
||||||
|
mut visit_selected: impl FnMut(
|
||||||
|
usize,
|
||||||
|
MemoryInterfacesBundleField<Expr<MemoryInterface<PhantomConst<MemoryInterfaceConfig>>>>,
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
let selected_output_interface = selected_output_interface.to_expr();
|
||||||
|
let mut else_scope = None;
|
||||||
|
for (i, &interface) in output_interfaces.fields().iter().enumerate() {
|
||||||
|
if i == output_interfaces.fields().len() - 1 {
|
||||||
|
// just else, no else if
|
||||||
|
visit_selected(i, interface);
|
||||||
|
} else {
|
||||||
|
let if_scope = fayalite::module::if_(selected_output_interface.cmp_eq(i));
|
||||||
|
visit_selected(i, interface);
|
||||||
|
else_scope = Some(if_scope.else_());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drop(else_scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl(no_static)]
|
||||||
|
struct Op<C: PhantomConstGet<MemoryInterfaceConfig>, OutputInterfaceCount: Size> {
|
||||||
|
start: MemoryOperationStart<C>,
|
||||||
|
output_interface_index: UIntInRangeType<ConstUsize<0>, OutputInterfaceCount>,
|
||||||
|
started_bytes: UIntInRangeInclusiveType<ConstUsize<0>, MemoryInterfaceBusWidthInBytes<C>>,
|
||||||
|
finished_bytes: UIntInRangeInclusiveType<ConstUsize<0>, MemoryInterfaceBusWidthInBytes<C>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
let op_ty = Op[input_interface_config][output_interfaces.fields().len()];
|
||||||
|
let option_op_ty = HdlOption[op_ty];
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
let not_started_op_queue = instance(queue(
|
||||||
|
op_ty,
|
||||||
|
const { NonZeroUsize::new(2).unwrap() },
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
));
|
||||||
|
|
||||||
|
connect(not_started_op_queue.cd, cd);
|
||||||
|
connect(
|
||||||
|
not_started_op_queue.inp.data,
|
||||||
|
HdlOption::map(input_interface.start.data, |start| {
|
||||||
|
#[hdl]
|
||||||
|
let output_interface_index = wire(op_ty.output_interface_index);
|
||||||
|
connect(
|
||||||
|
output_interface_index,
|
||||||
|
(output_interfaces.fields().len() - 1).cast_to(output_interface_index.ty()),
|
||||||
|
);
|
||||||
|
#[hdl]
|
||||||
|
Op::<_, _> {
|
||||||
|
start,
|
||||||
|
output_interface_index,
|
||||||
|
started_bytes: 0u8.cast_to(op_ty.started_bytes),
|
||||||
|
finished_bytes: 0u8.cast_to(op_ty.finished_bytes),
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
connect(input_interface.start.ready, not_started_op_queue.inp.ready);
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
let starting_op_reg = reg_builder().clock_domain(cd).reset(option_op_ty.HdlNone());
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
let starting_op_in = wire(option_op_ty);
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
if let HdlSome(_) = starting_op_reg {
|
||||||
|
connect(starting_op_in, starting_op_reg);
|
||||||
|
connect(not_started_op_queue.out.ready, false);
|
||||||
|
} else {
|
||||||
|
connect(starting_op_in, not_started_op_queue.out.data);
|
||||||
|
connect(not_started_op_queue.out.ready, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
for output_interface in output_interfaces.fields() {
|
||||||
|
connect(
|
||||||
|
output_interface.value.start.data,
|
||||||
|
output_interface.value.ty().start.data.HdlNone(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
if let HdlSome(starting_op_in) = starting_op_in {
|
||||||
|
#[hdl]
|
||||||
|
let Op::<_, _> {
|
||||||
|
start,
|
||||||
|
output_interface_index,
|
||||||
|
started_bytes,
|
||||||
|
finished_bytes,
|
||||||
|
} = starting_op_in;
|
||||||
|
#[hdl]
|
||||||
|
let MemoryOperationStart::<_> {
|
||||||
|
kind,
|
||||||
|
addr,
|
||||||
|
write_data,
|
||||||
|
rw_mask,
|
||||||
|
op_id,
|
||||||
|
config,
|
||||||
|
} = start;
|
||||||
|
visit_selected_output_interface(
|
||||||
|
&output_interfaces,
|
||||||
|
output_interface_index,
|
||||||
|
|_, output_interface| {
|
||||||
|
connect(
|
||||||
|
output_interface.value.start.data,
|
||||||
|
#[hdl]
|
||||||
|
MemoryOperationStart::<_> { kind },
|
||||||
|
);
|
||||||
|
#[hdl]
|
||||||
|
if output_interface.value.start {
|
||||||
|
todo();
|
||||||
|
}
|
||||||
|
todo!();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
connect(starting_op_reg, option_op_ty.HdlNone());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
let started_op_queue = instance(queue(
|
||||||
|
op_ty,
|
||||||
|
input_interface_config.get().queue_capacity,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
));
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
let finishing_op_reg = reg_builder().clock_domain(cd).reset(option_op_ty.HdlNone());
|
||||||
|
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue