diff --git a/Cargo.lock b/Cargo.lock index 67bc26e..2e1df0c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -207,8 +207,6 @@ name = "cpu" version = "0.1.0" dependencies = [ "fayalite", - "name_mangling_serde", - "serde", ] [[package]] @@ -302,7 +300,7 @@ checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" [[package]] name = "fayalite" version = "0.3.0" -source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#d453755bb2cd0b6f2340f3e49058d29a2ee279e8" +source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#bd75fdfefd642f6dd2210cfb003fa63f9dce114e" dependencies = [ "bitvec", "blake3", @@ -327,7 +325,7 @@ dependencies = [ [[package]] name = "fayalite-proc-macros" version = "0.3.0" -source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#d453755bb2cd0b6f2340f3e49058d29a2ee279e8" +source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#bd75fdfefd642f6dd2210cfb003fa63f9dce114e" dependencies = [ "fayalite-proc-macros-impl", ] @@ -335,7 +333,7 @@ dependencies = [ [[package]] name = "fayalite-proc-macros-impl" version = "0.3.0" -source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#d453755bb2cd0b6f2340f3e49058d29a2ee279e8" +source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#bd75fdfefd642f6dd2210cfb003fa63f9dce114e" dependencies = [ "base16ct", "num-bigint", @@ -350,7 +348,7 @@ dependencies = [ [[package]] name = "fayalite-visit-gen" version = "0.3.0" -source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#d453755bb2cd0b6f2340f3e49058d29a2ee279e8" +source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#bd75fdfefd642f6dd2210cfb003fa63f9dce114e" dependencies = [ "indexmap", "prettyplease", @@ -481,14 +479,6 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" -[[package]] -name = "name_mangling_serde" -version = "0.1.0" -dependencies = [ - "serde", - "serde_json", -] - [[package]] name = "num-bigint" version = "0.4.6" diff --git a/Cargo.toml b/Cargo.toml index 38cade6..5f7aded 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,10 +14,7 @@ categories = [] rust-version = "1.82.0" [workspace.dependencies] -name_mangling_serde = { version = "=0.1.0", path = "crates/name_mangling_serde" } fayalite = { git = "https://git.libre-chip.org/libre-chip/fayalite.git", version = "0.3.0", branch = "master" } -serde = { version = "1.0.202", features = ["derive"] } -serde_json = { version = "1.0.117", features = ["preserve_order"] } [profile.dev] opt-level = 1 diff --git a/crates/cpu/Cargo.toml b/crates/cpu/Cargo.toml index 9ff26c3..16ec0b9 100644 --- a/crates/cpu/Cargo.toml +++ b/crates/cpu/Cargo.toml @@ -16,8 +16,3 @@ version.workspace = true [dependencies] fayalite.workspace = true -serde.workspace = true -name_mangling_serde.workspace = true - -[lints.rust] -unexpected_cfgs = { level = "warn", check-cfg = ['cfg(todo)'] } diff --git a/crates/cpu/src/config.rs b/crates/cpu/src/config.rs index 6a0d27a..5be163c 100644 --- a/crates/cpu/src/config.rs +++ b/crates/cpu/src/config.rs @@ -1,17 +1,16 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information use crate::{ - instruction::{PRegNum, CONST_ZERO_UNIT_NUM}, - unit::UnitKind, + instruction::{MOpTrait, PRegNum, RenamedMOp, UnitNum, UnitOutRegNum, CONST_ZERO_UNIT_NUM}, + unit::{ + unit_base::{UnitForwardingInfo, UnitToRegAlloc}, + UnitCancelInput, UnitKind, UnitOutputWrite, + }, }; -use fayalite::{ - intern::{Intern, Interned}, - prelude::*, -}; -use serde::{Deserialize, Serialize}; +use fayalite::prelude::*; use std::num::NonZeroUsize; -#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)] +#[derive(Clone, Eq, PartialEq, Hash, Debug)] #[non_exhaustive] pub struct UnitConfig { pub kind: UnitKind, @@ -28,14 +27,15 @@ impl UnitConfig { } } -#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)] +#[derive(Clone, Eq, PartialEq, Hash, Debug)] #[non_exhaustive] pub struct CpuConfig { - pub units: Interned<[UnitConfig]>, + pub units: Vec, pub out_reg_num_width: usize, pub fetch_width: NonZeroUsize, /// default value for [`UnitConfig::max_in_flight`] pub default_unit_max_in_flight: NonZeroUsize, + pub rob_size: NonZeroUsize, } impl CpuConfig { @@ -52,12 +52,13 @@ impl CpuConfig { }; v }; - pub fn new(units: Interned<[UnitConfig]>) -> Self { + pub fn new(units: Vec, rob_size: NonZeroUsize) -> Self { Self { units, out_reg_num_width: Self::DEFAULT_OUT_REG_NUM_WIDTH, fetch_width: Self::DEFAULT_FETCH_WIDTH, default_unit_max_in_flight: Self::DEFAULT_UNIT_MAX_IN_FLIGHT, + rob_size, } } pub fn non_const_unit_nums(&self) -> std::ops::Range { @@ -66,83 +67,53 @@ impl CpuConfig { pub fn unit_num_width(&self) -> usize { UInt::range(CONST_ZERO_UNIT_NUM..self.non_const_unit_nums().end).width() } + pub fn unit_num(&self) -> UnitNum { + UnitNum[self.unit_num_width()] + } + pub fn unit_out_reg_num(&self) -> UnitOutRegNum { + UnitOutRegNum[self.out_reg_num_width] + } + pub fn p_reg_num(&self) -> PRegNum { + PRegNum[self.unit_num_width()][self.out_reg_num_width] + } + pub fn p_reg_num_width(&self) -> usize { + self.unit_num_width() + self.out_reg_num_width + } + pub fn renamed_mop_in_unit(&self) -> RenamedMOp, DynSize> { + RenamedMOp[self.unit_out_reg_num()][self.p_reg_num_width()] + } + pub fn unit_output_write(&self) -> UnitOutputWrite { + UnitOutputWrite[self.out_reg_num_width] + } + pub fn unit_output_writes(&self) -> Array>> { + Array[HdlOption[self.unit_output_write()]][self.non_const_unit_nums().len()] + } + pub fn unit_cancel_input(&self) -> UnitCancelInput { + UnitCancelInput[self.out_reg_num_width] + } + pub fn unit_forwarding_info(&self) -> UnitForwardingInfo { + UnitForwardingInfo[self.unit_num_width()][self.out_reg_num_width] + [self.non_const_unit_nums().len()] + } pub fn unit_max_in_flight(&self, unit_index: usize) -> NonZeroUsize { self.units[unit_index] .max_in_flight .unwrap_or(self.default_unit_max_in_flight) } - pub fn retire_queue_index_width(&self) -> usize { - let max_in_flight: usize = (0..self.units.len()) - .map(|unit_index| self.unit_max_in_flight(unit_index).get()) - .sum(); - 2 + max_in_flight.next_power_of_two().ilog2() as usize + pub fn unit_to_reg_alloc< + MOp: Type + MOpTrait, SrcRegWidth = DynSize>, + ExtraOut: Type, + >( + &self, + mop_ty: MOp, + extra_out_ty: ExtraOut, + ) -> UnitToRegAlloc { + assert_eq!( + mop_ty.dest_reg_ty(), + self.unit_out_reg_num(), + "inconsistent types", + ); + UnitToRegAlloc[mop_ty][extra_out_ty][self.unit_num_width()][self.out_reg_num_width] + [self.non_const_unit_nums().len()] } } - -mod sealed { - pub trait Sealed {} -} - -impl sealed::Sealed for PhantomConst {} - -pub trait CpuConfigType: Type + ToExpr + sealed::Sealed { - fn get(self) -> Interned; -} - -impl CpuConfigType for PhantomConst { - fn get(self) -> Interned { - self.get() - } -} - -pub trait Identity { - type SelfType: ?Sized; - type ArgType: ?Sized; -} - -impl Identity for T { - type SelfType = T; - type ArgType = Arg; -} - -macro_rules! impl_cpu_config_accessors { - ( - $( - #[without_generics = $without_generics:ident] - $vis:vis type $ident:ident<$T:ident> = |$arg:ident| $expr:expr; - )* - ) => { - $( - #[allow(non_camel_case_types)] - $vis struct $without_generics; - - #[allow(non_upper_case_globals)] - $vis const $ident: $without_generics = $without_generics; - - $vis type $ident<$T> = >::SelfType; - - impl<$T: CpuConfigType> std::ops::Index<$T> for $without_generics { - type Output = usize; - - fn index(&self, $arg: $T) -> &Self::Output { - Interned::into_inner(Intern::intern_sized($expr)) - } - } - )* - }; -} - -impl_cpu_config_accessors! { - #[without_generics = __UnitNumWidth_WithoutGenerics] - pub type UnitNumWidth = |arg| arg.get().unit_num_width(); - #[without_generics = __UnitOutRegNumWidth_WithoutGenerics] - pub type UnitOutRegNumWidth = |arg| arg.get().out_reg_num_width; - #[without_generics = __PRegNumWidth_WithoutGenerics] - pub type PRegNumWidth = |arg| PRegNum[arg].canonical().bit_width(); - #[without_generics = __RetireQueueIndexWidth_WithoutGenerics] - pub type RetireQueueIndexWidth = |arg| arg.get().retire_queue_index_width(); - #[without_generics = __UnitCount_WithoutGenerics] - pub type UnitCount = |arg| arg.get().non_const_unit_nums().len(); - #[without_generics = __FetchWidth_WithoutGenerics] - pub type FetchWidth = |arg| arg.get().fetch_width.get(); -} diff --git a/crates/cpu/src/instruction.rs b/crates/cpu/src/instruction.rs index 0d85ff9..80dd9d5 100644 --- a/crates/cpu/src/instruction.rs +++ b/crates/cpu/src/instruction.rs @@ -1,10 +1,6 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information -use crate::{ - config::{CpuConfigType, UnitNumWidth, UnitOutRegNumWidth}, - unit::UnitMOp, - util::range_u32_len, -}; +use crate::{unit::UnitMOp, util::range_u32_len}; use fayalite::{ expr::ops::{ArrayLiteral, ExprPartialEq}, intern::Interned, @@ -809,21 +805,19 @@ common_mop_struct! { } } -#[hdl(cmp_eq, no_static)] +#[hdl(cmp_eq)] /// there may be more than one unit of a given kind, so UnitNum is not the same as UnitKind. /// zero is used for built-in constants, such as the zero register -pub struct UnitNum { - pub adj_value: UIntType>, - pub config: C, +pub struct UnitNum { + pub adj_value: UIntType, } -impl UnitNum { +impl UnitNum { #[hdl] pub fn const_zero(self) -> Expr { #[hdl] UnitNum { adj_value: CONST_ZERO_UNIT_NUM.cast_to(self.adj_value), - config: self.config, } } #[hdl] @@ -831,7 +825,6 @@ impl UnitNum { #[hdl] UnitNum { adj_value: (index + 1).cast_to(self.adj_value), - config: self.config, } } pub fn is_index(expr: impl ToExpr, index: usize) -> Expr { @@ -842,7 +835,7 @@ impl UnitNum { .cmp_eq(expr.adj_value) } #[hdl] - pub fn as_index(expr: impl ToExpr) -> Expr>> { + pub fn as_index(expr: impl ToExpr) -> Expr>> { let expr = expr.to_expr(); #[hdl] let unit_index = wire(HdlOption[Expr::ty(expr).adj_value]); @@ -860,20 +853,19 @@ impl UnitNum { pub const CONST_ZERO_UNIT_NUM: usize = 0; -#[hdl(cmp_eq, no_static)] -pub struct UnitOutRegNum { - pub value: UIntType>, - pub config: C, +#[hdl(cmp_eq)] +pub struct UnitOutRegNum { + pub value: UIntType, } -#[hdl(cmp_eq, no_static)] +#[hdl(cmp_eq)] /// Physical Register Number -- registers in the CPU's backend -pub struct PRegNum { - pub unit_num: UnitNum, - pub unit_out_reg: UnitOutRegNum, +pub struct PRegNum { + pub unit_num: UnitNum, + pub unit_out_reg: UnitOutRegNum, } -impl PRegNum { +impl PRegNum { #[hdl] pub fn const_zero(self) -> Expr { #[hdl] @@ -882,7 +874,6 @@ impl PRegNum { unit_out_reg: #[hdl] UnitOutRegNum { value: 0u8.cast_to(self.unit_out_reg.value), - config: self.unit_out_reg.config, }, } } @@ -919,9 +910,11 @@ impl MOpRegNum { // // TODO: maybe add more registers later. pub const FLAG_REG_NUMS: Range = 0xFE..0x100; - /// registers that aren't constants - pub const NON_CONST_REG_NUMS: Range = - Self::CONST_ZERO_REG_NUM + 1..Self::FLAG_REG_NUMS.end; + /// registers handled by a special small rename table (for flags and stuff, since it has more read/write ports) + pub const SPECIAL_REG_NUMS: Range = Self::FLAG_REG_NUMS; + /// registers handled by the large rename table for normal registers (has less read/write ports) + pub const NORMAL_REG_NUMS: Range = + Self::CONST_ZERO_REG_NUM + 1..Self::SPECIAL_REG_NUMS.start; } #[hdl(cmp_eq)] @@ -936,6 +929,29 @@ pub struct MOpDestReg { pub flag_regs: Array, { range_u32_len(&MOpRegNum::FLAG_REG_NUMS) }>, } +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] +pub enum RenameTableName { + /// the large rename table for normal registers (has less read/write ports) + Normal, + /// a special small rename table (for flags and stuff, since it has more read/write ports) + Special, +} + +impl RenameTableName { + pub const fn reg_range(self) -> std::ops::Range { + match self { + Self::Normal => MOpRegNum::NORMAL_REG_NUMS, + Self::Special => MOpRegNum::SPECIAL_REG_NUMS, + } + } + pub const fn as_str(self) -> &'static str { + match self { + Self::Normal => "rename_table_normal", + Self::Special => "rename_table_special", + } + } +} + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub enum MOpDestRegKind { NormalReg { @@ -973,13 +989,16 @@ impl fmt::Display for MOpDestRegName { } impl MOpDestRegKind { - pub const fn reg_num_range(self) -> std::ops::Range { + pub const fn reg_range(self) -> std::ops::Range { match self { - Self::NormalReg { dest_reg_index: _ } => MOpRegNum::NON_CONST_REG_NUMS, - Self::FlagReg { - reg_num, - flag_reg_index: _, - } => reg_num..reg_num + 1, + Self::NormalReg { .. } => MOpRegNum::NORMAL_REG_NUMS, + Self::FlagReg { .. } => MOpRegNum::FLAG_REG_NUMS, + } + } + pub const fn rename_table_names(self) -> &'static [RenameTableName] { + match self { + Self::NormalReg { .. } => &[RenameTableName::Normal, RenameTableName::Special], + Self::FlagReg { .. } => &[RenameTableName::Special], } } pub fn fixed_reg_num(self) -> Option { @@ -1072,5 +1091,5 @@ pub type MOp = UnitMOp< >; #[hdl] -pub type RenamedMOp = - UnitMOp<(), SrcRegWidth, L2RegisterFileMOp<(), SrcRegWidth>>; +pub type RenamedMOp = + UnitMOp>; diff --git a/crates/cpu/src/instruction_rename.rs b/crates/cpu/src/instruction_rename.rs deleted file mode 100644 index 8acca1b..0000000 --- a/crates/cpu/src/instruction_rename.rs +++ /dev/null @@ -1,261 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-or-later -// See Notices.txt for copyright information - -use crate::{ - config::{CpuConfig, CpuConfigType, FetchWidth, PRegNumWidth}, - instruction::{MOp, MOpDestReg, MOpRegNum, MOpTrait, MoveRegMOp, PRegNum, RenamedMOp}, - rename_table::{rename_table, RenameTablePortConfig}, - unit::{RenamedInsnData, RetireQueueIndex, UnitMOp}, - util::array_vec::{ArrayVec, Length, ReadyValidArray}, -}; -use fayalite::{ - prelude::*, - util::{prefix_sum::PrefixSumAlgorithm, ready_valid::ReadyValid}, -}; - -#[hdl(no_static)] -pub struct InstructionRenameInputInsn { - pub mop: MOp, - pub pc: UInt<64>, - pub renamed_dest: PRegNum, -} - -#[hdl(no_static)] -struct InsnsInPrefixSummary { - all_ready: Bool, - ready_count: Length>, - retire_queue_used: Length>, - config: C, -} - -type C = PhantomConst; - -#[hdl] -pub type InstructionRenameInsnsOut = ArrayType< - ReadyValid>, PRegNum>>, - FetchWidth, ->; - -#[hdl_module] -pub fn instruction_rename(config: PhantomConst) { - #[hdl] - let cd: ClockDomain = m.input(); - #[hdl] - let insns_in: ReadyValidArray, FetchWidth> = - m.input(ReadyValidArray[InstructionRenameInputInsn[config]][FetchWidth[config]]); - #[hdl] - let start_retire_queue_index: RetireQueueIndex = m.input(RetireQueueIndex[config]); - #[hdl] - let end_retire_queue_index: RetireQueueIndex = m.output(RetireQueueIndex[config]); - #[hdl] - let insns_out: InstructionRenameInsnsOut = m.output(InstructionRenameInsnsOut[config]); - - // TODO: handle resetting table after cancelling instructions - - #[hdl] - let insns_ready_or_move = wire(ArrayType[Bool][FetchWidth[config]]); - - for (insn_ready_or_move, insn_out) in insns_ready_or_move.into_iter().zip(insns_out) { - connect(insn_ready_or_move, insn_out.ready); - } - - ArrayVec::for_each(insns_in.data, |fetch_index, input_insn| { - #[hdl] - match input_insn.mop { - UnitMOp::<_, _, _>::TransformedMove(_) => { - connect(insns_ready_or_move[fetch_index], true); - } - UnitMOp::<_, _, _>::AluBranch(_) | UnitMOp::<_, _, _>::LoadStore(_) => {} - } - }); - - let insns_in_prefix_summary_ty = InsnsInPrefixSummary[config]; - #[hdl] - let insns_in_prefix_summaries = wire(ArrayType[insns_in_prefix_summary_ty][FetchWidth[config]]); - let insns_in_prefix_summaries_vec = PrefixSumAlgorithm::WorkEfficient.run( - (0..FetchWidth[config]).map(|fetch_index| { - #[hdl] - let insns_in_prefix_summary_in = wire(insns_in_prefix_summary_ty); - #[hdl] - let InsnsInPrefixSummary::<_> { - all_ready, - ready_count, - retire_queue_used, - config: _, - } = insns_in_prefix_summary_in; - connect(all_ready, insns_out[fetch_index].ready); - connect( - ready_count, - Expr::ty(ready_count).cast_from_uint_unchecked(all_ready.cast_to(UInt[1])), - ); - connect(retire_queue_used, Expr::ty(retire_queue_used).zero()); - #[hdl] - if let HdlSome(input_insn) = ArrayVec::get(insns_in.data, fetch_index) { - connect(retire_queue_used, ready_count); - #[hdl] - match input_insn.mop { - UnitMOp::<_, _, _>::TransformedMove(_) => { - connect(all_ready, true); - } - UnitMOp::<_, _, _>::AluBranch(_) | UnitMOp::<_, _, _>::LoadStore(_) => {} - } - } - insns_in_prefix_summary_in - }), - |l, r| { - #[hdl] - let insns_in_prefix_summary_merge = wire(insns_in_prefix_summary_ty); - #[hdl] - let InsnsInPrefixSummary::<_> { - all_ready, - ready_count, - retire_queue_used, - config: _, - } = insns_in_prefix_summary_merge; - connect(all_ready, l.all_ready & r.all_ready); - #[hdl] - if l.all_ready { - connect( - ready_count, - Expr::ty(ready_count).cast_from_uint_unchecked( - Length::as_uint(l.ready_count) + Length::as_uint(r.ready_count), - ), - ); - connect( - retire_queue_used, - Expr::ty(retire_queue_used).cast_from_uint_unchecked( - Length::as_uint(l.retire_queue_used) + Length::as_uint(r.retire_queue_used), - ), - ); - } else { - connect(ready_count, l.ready_count); - connect(retire_queue_used, l.retire_queue_used); - } - insns_in_prefix_summary_merge - }, - ); - for (l, r) in insns_in_prefix_summaries - .into_iter() - .zip(insns_in_prefix_summaries_vec) - { - connect(l, r); - } - connect( - insns_in.ready, - insns_in_prefix_summaries[FetchWidth[config] - 1].ready_count, - ); - - #[hdl] - let retire_queue_indexes = wire(Array[RetireQueueIndex[config]][FetchWidth[config] + 1]); - connect(retire_queue_indexes[0], start_retire_queue_index); - connect( - end_retire_queue_index, - retire_queue_indexes[FetchWidth[config]], - ); - for (retire_queue_index, insns_in_prefix_summary) in retire_queue_indexes - .into_iter() - .skip(1) - .zip(insns_in_prefix_summaries) - { - connect_any( - retire_queue_index.index, - start_retire_queue_index.index - + Length::as_uint(insns_in_prefix_summary.retire_queue_used), - ); - } - - let mut port_configs = Vec::new(); - let mut src_reg_count = 0; - MOpTrait::for_each_src_reg(MOp.uninit(), &mut |_, src_index| { - src_reg_count = src_reg_count.max(src_index + 1); - }); - for _ in 0..FetchWidth[config] { - for _ in 0..src_reg_count { - port_configs.push(RenameTablePortConfig::Read { - addr_range: MOpRegNum::NON_CONST_REG_NUMS, - }); - } - for dest_reg_kind in MOpDestReg::REG_KINDS { - port_configs.push(RenameTablePortConfig::Write { - addr_range: dest_reg_kind.reg_num_range(), - }); - } - } - - #[hdl] - let rename_table = instance(rename_table(config, &port_configs)); - - connect(rename_table.cd, cd); - - for read_port in rename_table.read_ports { - connect_any(read_port.addr, 0_hdl_u0); - } - for write_port in rename_table.write_ports { - connect_any(write_port.addr, 0_hdl_u0); - connect_any(write_port.data, PRegNum[config].const_zero()); - } - - ArrayVec::for_each( - ReadyValidArray::firing_data(insns_in), - |fetch_index, input_insn| { - let read_port_index = fetch_index * src_reg_count; - let write_port_index = fetch_index * MOpDestReg::REG_COUNT; - #[hdl] - let InstructionRenameInputInsn::<_> { - mop, - pc, - renamed_dest, - } = input_insn; - let insn_out = - MOpTrait::map_regs(mop, (), PRegNumWidth[config], &mut |src_reg, src_index| { - connect( - rename_table.read_ports[read_port_index + src_index].addr, - src_reg.cast_bits_to(MOpRegNum), - ); - rename_table.read_ports[read_port_index + src_index] - .data - .cast_to_bits() - }); - for (i, dest_reg) in MOpDestReg::regs(MOpTrait::dest_reg(mop)) - .into_iter() - .enumerate() - { - connect( - rename_table.write_ports[write_port_index + i].addr, - dest_reg, - ); - connect( - rename_table.write_ports[write_port_index + i].data, - renamed_dest, - ); - } - let insn_out = UnitMOp::try_with_transformed_move_op( - insn_out, - RenamedMOp[PRegNumWidth[config]].TransformedMove, - |insn_out: Expr>, move_reg: Expr>| { - for i in 0..MOpDestReg::REG_COUNT { - // execute move by using same PRegNum as src[0] for dest - connect( - rename_table.write_ports[write_port_index + i].data, - move_reg.common.src[0].cast_bits_to(PRegNum[config]), - ); - } - // move already executed, so remove it - connect(insn_out, Expr::ty(insn_out).HdlNone()); - }, - ); - connect( - insns_out[fetch_index].data, - HdlOption::map(insn_out, |insn_out| { - #[hdl] - RenamedInsnData::<_, _, _> { - retire_queue_index: retire_queue_indexes[fetch_index], - pc, - dest: renamed_dest, - mop: insn_out, - } - }), - ); - }, - ); -} diff --git a/crates/cpu/src/lib.rs b/crates/cpu/src/lib.rs index f561b5d..bae3720 100644 --- a/crates/cpu/src/lib.rs +++ b/crates/cpu/src/lib.rs @@ -2,10 +2,7 @@ // See Notices.txt for copyright information pub mod config; pub mod instruction; -pub mod instruction_rename; pub mod reg_alloc; pub mod register; -pub mod rename_table; -pub mod retire_queue; pub mod unit; pub mod util; diff --git a/crates/cpu/src/reg_alloc.rs b/crates/cpu/src/reg_alloc.rs index acf796d..6e7bc5d 100644 --- a/crates/cpu/src/reg_alloc.rs +++ b/crates/cpu/src/reg_alloc.rs @@ -3,17 +3,17 @@ use crate::{ config::CpuConfig, instruction::{ - MOp, MOpDestReg, MOpRegNum, MOpTrait, MoveRegMOp, PRegNum, UnitOutRegNum, + MOp, MOpDestReg, MOpRegNum, MOpTrait, MoveRegMOp, PRegNum, RenameTableName, UnitOutRegNum, COMMON_MOP_SRC_LEN, }, unit::{ + unit_base::{UnitForwardingInfo, UnitInput}, GlobalState, TrapData, UnitMOp, UnitOutput, UnitOutputWrite, UnitResult, UnitResultCompleted, UnitTrait, }, - util::array_vec::ReadyValidArray, + util::tree_reduce::tree_reduce_with_state, }; use fayalite::{ - int::BoolOrIntType, memory::{splat_mask, WriteStruct}, module::{instance_with_loc, memory_with_loc, wire_with_loc}, prelude::*, @@ -44,12 +44,150 @@ pub enum FetchDecodeSpecialOp { #[hdl] pub struct FetchDecodeInterface { - pub decoded_insns: ReadyValidArray, + pub decoded_insns: ArrayType, FetchWidth>, #[hdl(flip)] pub fetch_decode_special_op: ReadyValid, } -#[cfg(todo)] +#[hdl] +struct ROBRenamedInsn { + mop_dest: MOpDestReg, + p_dest: PRegNum, +} + +#[hdl] +struct ROBEntry { + renamed_insn: ROBRenamedInsn, + dest_written: Bool, +} + +#[hdl_module] +fn rob(config: &CpuConfig) { + #[hdl] + let cd: ClockDomain = m.input(); + #[hdl] + let renamed_insns_in: Array>> = m.input( + Array[ReadyValid[ROBRenamedInsn[config.unit_num_width()][config.out_reg_num_width]]] + [config.fetch_width.get()], + ); + #[hdl] + let unit_forwarding_info: UnitForwardingInfo = + m.input(config.unit_forwarding_info()); + + let rob_entry_ty = ROBEntry[config.unit_num_width()][config.out_reg_num_width]; + #[hdl] + let rob = reg_builder() + .clock_domain(cd) + .no_reset(Array[rob_entry_ty][config.rob_size.get()]); + #[hdl] + let rob_valid_start = reg_builder() + .clock_domain(cd) + .reset(UInt::range(0..config.rob_size.get()).zero()); + #[hdl] + let rob_valid_end = reg_builder() + .clock_domain(cd) + .reset(UInt::range(0..config.rob_size.get()).zero()); + #[hdl] + let free_space = wire(UInt::range_inclusive(0..=config.rob_size.get())); + #[hdl] + if rob_valid_end.cmp_lt(rob_valid_start) { + // rob_valid_end wrapped around but start didn't + connect_any( + free_space, + rob_valid_end + config.rob_size.get() - rob_valid_start, + ); + } else { + connect_any(free_space, rob_valid_end - rob_valid_start); + } + + struct IndexAndRange { + index: Expr, + range: std::ops::Range, + } + + let mut next_write_index = IndexAndRange { + index: rob_valid_end, + range: 0..config.rob_size.get(), + }; + for fetch_index in 0..config.fetch_width.get() { + let write_index = next_write_index; + let next_write_index_range = write_index.range.start..write_index.range.end + 1; + next_write_index = IndexAndRange { + index: wire_with_loc( + &format!("next_write_index_{fetch_index}"), + SourceLocation::caller(), + UInt::range(next_write_index_range.clone()), + ), + range: next_write_index_range, + }; + connect( + renamed_insns_in[fetch_index].ready, + fetch_index.cmp_lt(free_space), + ); + #[hdl] + if let HdlSome(renamed_insn) = ReadyValid::firing_data(renamed_insns_in[fetch_index]) { + for i in write_index.range.clone() { + #[hdl] + if write_index.index.cmp_eq(i) { + connect( + rob[i % config.rob_size.get()], + #[hdl] + ROBEntry { + renamed_insn, + dest_written: false, + }, + ); + } + } + } + // TODO: optimize write_index chain better + connect_any( + next_write_index.index, + write_index.index + + ReadyValid::firing(renamed_insns_in[fetch_index]).cast_to_static::>(), + ); + } + assert!( + config.rob_size >= config.fetch_width, + "rob_size ({}) is too small for fetch_width = {} -- next_write_index would overflow", + config.rob_size, + config.fetch_width, + ); + #[hdl] + if next_write_index.index.cmp_lt(config.rob_size.get()) { + connect_any(rob_valid_end, next_write_index.index); + } else { + connect_any( + rob_valid_end, + next_write_index.index - config.rob_size.get(), + ); + } + + // TODO: optimize better, O(rob_size * unit_count) is too big here + for rob_index in 0..config.rob_size.get() { + for unit_index in 0..config.non_const_unit_nums().len() { + #[hdl] + if let HdlSome(unit_output_write) = unit_forwarding_info.unit_output_writes[unit_index] + { + #[hdl] + let UnitOutputWrite::<_> { + which: unit_out_reg, + value: _, + } = unit_output_write; + let p_reg_num = #[hdl] + PRegNum::<_, _> { + unit_num: config.unit_num().from_index(unit_index), + unit_out_reg, + }; + #[hdl] + if rob[rob_index].renamed_insn.p_dest.cmp_eq(p_reg_num) { + connect(rob[rob_index].dest_written, true); + } + } + } + } +} + #[hdl_module] /// combination register allocator, register renaming, unit selection, and retire handling pub fn reg_alloc(config: &CpuConfig) { @@ -67,6 +205,10 @@ pub fn reg_alloc(config: &CpuConfig) { ); // TODO: finish + #[hdl] + let rob = instance(rob(config)); + connect(rob.cd, cd); + let mut rename_table_mems = BTreeMap::>::new(); for reg_kind in MOpDestReg::REG_KINDS { @@ -96,6 +238,11 @@ pub fn reg_alloc(config: &CpuConfig) { #[hdl] let renamed_mops_out_reg = wire(Array[HdlOption[config.p_reg_num()]][config.fetch_width.get()]); for fetch_index in 0..config.fetch_width.get() { + // TODO: finish + connect( + rob.renamed_insns_in[fetch_index].data, + Expr::ty(rob).renamed_insns_in.element().data.HdlNone(), + ); // TODO: finish connect( fetch_decode_interface.decoded_insns[fetch_index].ready, @@ -336,6 +483,7 @@ pub fn reg_alloc(config: &CpuConfig) { ); #[hdl] let unit_forwarding_info = wire(config.unit_forwarding_info()); + connect(rob.unit_forwarding_info, unit_forwarding_info); for (unit_index, unit_config) in config.units.iter().enumerate() { let dyn_unit = unit_config.kind.unit(config, unit_index); let unit = instance_with_loc( diff --git a/crates/cpu/src/reg_alloc/unit_free_regs_tracker.rs b/crates/cpu/src/reg_alloc/unit_free_regs_tracker.rs index 133049a..d19cf2a 100644 --- a/crates/cpu/src/reg_alloc/unit_free_regs_tracker.rs +++ b/crates/cpu/src/reg_alloc/unit_free_regs_tracker.rs @@ -1,10 +1,7 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information -use fayalite::{ - module::wire_with_loc, - prelude::*, - util::{prefix_sum::reduce, ready_valid::ReadyValid}, -}; +use crate::util::tree_reduce::tree_reduce; +use fayalite::{module::wire_with_loc, prelude::*, util::ready_valid::ReadyValid}; use std::{num::NonZeroUsize, ops::Range}; #[hdl_module] @@ -47,7 +44,7 @@ pub fn unit_free_regs_tracker( count, count_overflowed, alloc_nums, - }) = reduce( + }) = tree_reduce( (0..reg_count).map(|index| Summary { range: index..index + 1, count: (!allocated_reg[index]) diff --git a/crates/cpu/src/rename_table.rs b/crates/cpu/src/rename_table.rs deleted file mode 100644 index a38e4aa..0000000 --- a/crates/cpu/src/rename_table.rs +++ /dev/null @@ -1,187 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-or-later -// See Notices.txt for copyright information - -use crate::{ - config::{CpuConfig, CpuConfigType}, - instruction::{MOpRegNum, PRegNum}, - util::range_intersection, -}; -use fayalite::{ - memory::{splat_mask, ReadStruct, WriteStruct}, - module::memory_with_loc, - prelude::*, -}; -use std::{mem, ops::Range}; - -#[hdl(no_static)] -pub struct RenameTableReadPort { - pub addr: MOpRegNum, - #[hdl(flip)] - pub data: PRegNum, -} - -#[hdl(no_static)] -pub struct RenameTableWritePort { - pub addr: MOpRegNum, - pub data: PRegNum, -} - -#[derive(Clone, Debug)] -pub enum RenameTablePortConfig { - Read { addr_range: Range }, - Write { addr_range: Range }, -} - -type C = PhantomConst; - -/// register rename table. -/// all read/write operations are done in the order of `port_configs`. -/// So if `port_configs[0]` is a write and `port_configs[1]` is a read, -/// then the read port will combinatorially return data written by the -/// write port in the *same* clock cycle. However, if `port_configs[0]` -/// is a read and `port_configs[1]` is a write, then the read port will -/// not see the data written by the write port until the *next* clock cycle. -#[hdl_module] -pub fn rename_table(config: PhantomConst, port_configs: &[RenameTablePortConfig]) { - let read_count = port_configs - .iter() - .filter(|v| matches!(v, RenameTablePortConfig::Read { .. })) - .count(); - let write_count = port_configs - .iter() - .filter(|v| matches!(v, RenameTablePortConfig::Write { .. })) - .count(); - - #[hdl] - let cd: ClockDomain = m.input(); - #[hdl] - let read_ports: Array> = - m.input(Array[RenameTableReadPort[config]][read_count]); - #[hdl] - let write_ports: Array> = - m.input(Array[RenameTableWritePort[config]][write_count]); - - for read_port in read_ports { - connect(read_port.data, PRegNum[config].const_zero()); - } - - let port_configs_and_indexes = port_configs.iter().scan( - (0usize, 0), - |(read_port_index, write_port_index), port_config| { - Some(( - port_config, - match port_config { - RenameTablePortConfig::Read { .. } => { - mem::replace(read_port_index, *read_port_index + 1) - } - RenameTablePortConfig::Write { .. } => { - mem::replace(write_port_index, *write_port_index + 1) - } - }, - )) - }, - ); - - let mut range_transitions = Vec::with_capacity(port_configs.len() * 2); - for port_config in port_configs { - let (RenameTablePortConfig::Read { addr_range } - | RenameTablePortConfig::Write { addr_range }) = port_config; - range_transitions.push(addr_range.start); - range_transitions.push(addr_range.end); - } - range_transitions.sort_unstable(); - range_transitions.dedup(); - let mut last_range_transition = None; - for range_transition in range_transitions { - let Some(last_range_transition) = last_range_transition.replace(range_transition) else { - continue; - }; - let cur_addr_range = last_range_transition..range_transition; - let mut mem = memory_with_loc( - &if cur_addr_range.len() == 1 { - format!("mem_{:#x}", cur_addr_range.start) - } else { - format!("mem_{:#x}_{:#x}", cur_addr_range.start, cur_addr_range.end) - }, - PRegNum[config], - SourceLocation::caller(), - ); - mem.depth(cur_addr_range.len()); - let addr_in_range = |addr: Expr| { - if cur_addr_range.len() == 1 { - addr.value.cmp_eq(cur_addr_range.start) - } else { - addr.value.cmp_ge(cur_addr_range.start) & addr.value.cmp_lt(cur_addr_range.end) - } - }; - for (port_config, port_index) in port_configs_and_indexes.clone() { - match port_config { - RenameTablePortConfig::Read { addr_range } => { - if range_intersection(&addr_range, &cur_addr_range).is_none() { - continue; - } - let port = read_ports[port_index]; - #[hdl] - let ReadStruct::<_, _> { - addr, - en, - clk, - data, - } = mem.new_read_port(); - connect_any(addr, port.addr.value - cur_addr_range.start); - connect(en, addr_in_range(port.addr)); - connect(clk, cd.clk); - #[hdl] - if en { - connect(port.data, data); - } - } - RenameTablePortConfig::Write { addr_range } => { - if range_intersection(&addr_range, &cur_addr_range).is_none() { - continue; - } - let port = write_ports[port_index]; - #[hdl] - let WriteStruct::<_, _> { - addr, - en, - clk, - data, - mask, - } = mem.new_write_port(); - connect_any(addr, port.addr.value - cur_addr_range.start); - connect(en, addr_in_range(port.addr)); - connect(clk, cd.clk); - connect(data, port.data); - connect(mask, splat_mask(Expr::ty(port).data, true.to_expr())); - } - } - } - } - for (port_config_index, (port_config, port_index)) in - port_configs_and_indexes.clone().enumerate() - { - let RenameTablePortConfig::Read { addr_range } = port_config else { - continue; - }; - let port = read_ports[port_index]; - for (prev_port_config, prev_port_index) in - port_configs_and_indexes.clone().take(port_config_index) - { - let RenameTablePortConfig::Write { - addr_range: prev_addr_range, - } = prev_port_config - else { - continue; - }; - if range_intersection(addr_range, prev_addr_range).is_none() { - continue; - } - let prev_port = write_ports[prev_port_index]; - #[hdl] - if prev_port.addr.cmp_eq(port.addr) { - connect(port.data, prev_port.data); - } - } - } -} diff --git a/crates/cpu/src/retire_queue.rs b/crates/cpu/src/retire_queue.rs deleted file mode 100644 index 7fccb87..0000000 --- a/crates/cpu/src/retire_queue.rs +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-or-later -// See Notices.txt for copyright information - -use crate::{ - config::{CpuConfig, CpuConfigType}, - instruction::{MOpDestReg, PRegNum}, - unit::RetireQueueIndex, -}; -use fayalite::prelude::*; - -#[hdl(no_static)] -pub struct RenameRetireInterface { - pub start_retire_queue_index: RetireQueueIndex, - #[hdl(flip)] - pub end_retire_queue_index: RetireQueueIndex, -} - -#[hdl(no_static)] -pub struct RetireQueueEntry { - pub mop_dest: MOpDestReg, - pub renamed_dest: PRegNum, -} - -type C = PhantomConst; - -#[hdl_module] -pub fn retire_queue(config: PhantomConst) { - #[hdl] - let cd: ClockDomain = m.input(); - #[hdl] - let rename_retire_interface: RenameRetireInterface = m.output(RenameRetireInterface[config]); - todo!(); -} diff --git a/crates/cpu/src/unit.rs b/crates/cpu/src/unit.rs index 80bde55..cc11c55 100644 --- a/crates/cpu/src/unit.rs +++ b/crates/cpu/src/unit.rs @@ -2,21 +2,19 @@ // See Notices.txt for copyright information use crate::{ - config::{CpuConfig, CpuConfigType, RetireQueueIndexWidth, UnitCount}, + config::CpuConfig, instruction::{ mop_enum, AluBranchMOp, LoadStoreMOp, MOp, MOpDestReg, MOpInto, MOpRegNum, MOpTrait, RenamedMOp, UnitOutRegNum, }, register::{FlagsMode, PRegValue}, + unit::unit_base::UnitToRegAlloc, }; use fayalite::{ bundle::{Bundle, BundleType}, - int::BoolOrIntType, intern::{Intern, Interned}, prelude::*, - util::ready_valid::ReadyValid, }; -use serde::{Deserialize, Serialize}; pub mod alu_branch; pub mod unit_base; @@ -38,7 +36,7 @@ macro_rules! all_units { } ) => { $(#[$enum_meta])* - #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Serialize, Deserialize)] + #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] $vis enum $UnitKind { $( $(#[$variant_meta])* @@ -47,7 +45,7 @@ macro_rules! all_units { } impl $UnitKind { - pub fn unit(self, config: PhantomConst, unit_index: usize) -> DynUnit { + pub fn unit(self, config: &CpuConfig, unit_index: usize) -> DynUnit { match self { $($UnitKind::$Unit => $create_dyn_unit_fn(config, unit_index),)* } @@ -206,28 +204,23 @@ macro_rules! all_units { })* }; - const _: () = { - #[hdl] - type $DestReg = (); + $(impl<$DestReg: Type, $SrcRegWidth: Size> MOpInto> for $BeforeOp { + fn mop_into_ty(self) -> RenamedMOp<$DestReg, $SrcRegWidth> { + RenamedMOp[MOpTrait::dest_reg_ty(self)][MOpTrait::src_reg_width(self)] + } + fn mop_into(this: Expr) -> Expr> { + MOpInto::>::mop_into_ty(Expr::ty(this)).$BeforeUnit(this) + } + })* - $(impl<$SrcRegWidth: Size> MOpInto> for $BeforeOp { - fn mop_into_ty(self) -> RenamedMOp<$SrcRegWidth> { - RenamedMOp[MOpTrait::src_reg_width(self)] - } - fn mop_into(this: Expr) -> Expr> { - MOpInto::>::mop_into_ty(Expr::ty(this)).$BeforeUnit(this) - } - })* - - $(impl<$SrcRegWidth: Size> MOpInto> for $AfterOp { - fn mop_into_ty(self) -> RenamedMOp<$SrcRegWidth> { - RenamedMOp[MOpTrait::src_reg_width(self)] - } - fn mop_into(this: Expr) -> Expr> { - MOpInto::>::mop_into_ty(Expr::ty(this)).$AfterUnit(this) - } - })* - }; + $(impl<$DestReg: Type, $SrcRegWidth: Size> MOpInto> for $AfterOp { + fn mop_into_ty(self) -> RenamedMOp<$DestReg, $SrcRegWidth> { + RenamedMOp[MOpTrait::dest_reg_ty(self)][MOpTrait::src_reg_width(self)] + } + fn mop_into(this: Expr) -> Expr> { + MOpInto::>::mop_into_ty(Expr::ty(this)).$AfterUnit(this) + } + })* }; } @@ -260,80 +253,18 @@ pub struct GlobalState { pub flags_mode: FlagsMode, } -/// index into the retire queue (the queue of instructions that haven't yet retired) -#[hdl(cmp_eq, no_static)] -pub struct RetireQueueIndex { - /// increases by one for each instruction added to the retire queue. - /// - /// this wraps around, so you must not compare it using `cmp_lt`/`cmp_gt` - /// but instead must use [`Self::insns_until`] and compare the output with zero. - pub index: UIntType>, - pub config: C, -} - -impl RetireQueueIndex { - pub fn insns_until( - this: impl ToExpr, - target: impl ToExpr, - ) -> Expr>> { - let this = this.to_expr(); - let target = target.to_expr(); - assert_eq!(Expr::ty(this), Expr::ty(target)); - (this.index - target.index).cast_to(Expr::ty(this).index.as_same_width_sint()) - } -} - -#[hdl(no_static)] -pub struct RenamedInsnData { - pub retire_queue_index: RetireQueueIndex, - pub pc: UInt<64>, - pub dest: DestReg, - pub mop: MOp, -} - -#[hdl(no_static)] -pub struct UnitForwardingInfo { - pub unit_output_writes: ArrayType>, UnitCount>, - pub unit_reg_frees: ArrayType>, UnitCount>, -} - -#[hdl(no_static)] -pub struct UnitToRegAlloc { - #[hdl(flip)] - pub unit_forwarding_info: UnitForwardingInfo, - #[hdl(flip)] - pub input: ReadyValid>>, - #[hdl(flip)] - pub cancel_input: HdlOption>, - pub output: HdlOption>, - pub ready_for_retire_queue_index: HdlOption>, -} - -impl UnitToRegAlloc { - pub fn mop_ty(self) -> MOp { - self.input.data.HdlSome.mop - } - pub fn extra_out_ty(self) -> ExtraOut { - self.output.HdlSome.extra_out_ty() - } -} - #[hdl(cmp_eq)] pub struct UnitResultCompleted { pub value: PRegValue, pub extra_out: ExtraOut, } -#[hdl(cmp_eq, no_static)] -pub struct UnitOutputWrite { - pub dest: UnitOutRegNum, +#[hdl(cmp_eq)] +pub struct UnitOutputWrite { + pub which: UnitOutRegNum, pub value: PRegValue, } -#[hdl] -pub type UnitOutputWrites = - ArrayType>, UnitCount>; - #[hdl(cmp_eq)] pub struct TrapData { // TODO @@ -351,31 +282,21 @@ impl UnitResult { } } -#[hdl(no_static)] -pub struct UnitOutput { - pub dest: UnitOutRegNum, - pub retire_queue_index: RetireQueueIndex, +#[hdl] +pub struct UnitOutput { + pub which: UnitOutRegNum, pub result: UnitResult, } -impl UnitOutput { +impl UnitOutput { pub fn extra_out_ty(self) -> ExtraOut { self.result.extra_out_ty() } } -#[hdl(cmp_eq, no_static)] -pub struct UnitCancelInput { - pub target: RetireQueueIndex, -} - -impl UnitCancelInput { - pub fn is_canceled( - this: impl ToExpr, - insn_retire_queue_index: impl ToExpr>, - ) -> Expr { - RetireQueueIndex::insns_until(insn_retire_queue_index, this.to_expr().target).cmp_ge(0i8) - } +#[hdl(cmp_eq)] +pub struct UnitCancelInput { + pub which: UnitOutRegNum, } pub trait UnitTrait: @@ -391,14 +312,17 @@ pub trait UnitTrait: fn unit_kind(&self) -> UnitKind; - fn extract_mop(&self, mop: Expr>) -> Expr>; + fn extract_mop( + &self, + mop: Expr, DynSize>>, + ) -> Expr>; fn module(&self) -> Interned>; fn unit_to_reg_alloc( &self, this: Expr, - ) -> Expr, Self::MOp, Self::ExtraOut>>; + ) -> Expr>; fn cd(&self, this: Expr) -> Expr; @@ -446,7 +370,10 @@ impl UnitTrait for DynUnit { self.unit_kind } - fn extract_mop(&self, mop: Expr>) -> Expr> { + fn extract_mop( + &self, + mop: Expr, DynSize>>, + ) -> Expr> { self.unit.extract_mop(mop) } @@ -457,7 +384,7 @@ impl UnitTrait for DynUnit { fn unit_to_reg_alloc( &self, this: Expr, - ) -> Expr, Self::MOp, Self::ExtraOut>> { + ) -> Expr> { self.unit.unit_to_reg_alloc(this) } @@ -498,7 +425,10 @@ impl UnitTrait for DynUnitWrapper>) -> Expr> { + fn extract_mop( + &self, + mop: Expr, DynSize>>, + ) -> Expr> { Expr::from_enum(Expr::as_enum(self.0.extract_mop(mop))) } @@ -509,7 +439,7 @@ impl UnitTrait for DynUnitWrapper, - ) -> Expr, Self::MOp, Self::ExtraOut>> { + ) -> Expr> { Expr::from_bundle(Expr::as_bundle( self.0.unit_to_reg_alloc(Expr::from_bundle(this)), )) diff --git a/crates/cpu/src/unit/alu_branch.rs b/crates/cpu/src/unit/alu_branch.rs index 63d85af..082fd8d 100644 --- a/crates/cpu/src/unit/alu_branch.rs +++ b/crates/cpu/src/unit/alu_branch.rs @@ -2,26 +2,29 @@ // See Notices.txt for copyright information use crate::{ - config::{CpuConfig, PRegNumWidth}, + config::CpuConfig, instruction::{ - AddSubMOp, AluBranchMOp, AluCommonMOp, CommonMOp, LogicalMOp, OutputIntegerMode, - RenamedMOp, COMMON_MOP_SRC_LEN, + AddSubMOp, AluBranchMOp, AluCommonMOp, CommonMOp, LogicalMOp, MOpTrait, OutputIntegerMode, + RenamedMOp, UnitOutRegNum, COMMON_MOP_SRC_LEN, }, register::{FlagsMode, PRegFlagsPowerISA, PRegFlagsX86, PRegValue}, unit::{ - unit_base::{unit_base, ExecuteEnd, ExecuteStart}, + unit_base::{unit_base, ExecuteEnd, ExecuteStart, UnitToRegAlloc}, DynUnit, DynUnitWrapper, GlobalState, UnitKind, UnitMOp, UnitOutput, UnitResult, - UnitResultCompleted, UnitToRegAlloc, UnitTrait, + UnitResultCompleted, UnitTrait, }, }; use fayalite::{ - intern::Interned, module::wire_with_loc, prelude::*, util::ready_valid::ReadyValid, + intern::{Intern, Interned}, + module::wire_with_loc, + prelude::*, + util::ready_valid::ReadyValid, }; use std::{collections::HashMap, ops::RangeTo}; #[hdl] fn add_sub( - mop: Expr>, + mop: Expr, DynSize, SrcCount>>, pc: Expr>, flags_mode: Expr, src_values: Expr>, @@ -229,7 +232,7 @@ fn add_sub( #[hdl] fn logical( - mop: Expr>, + mop: Expr, DynSize>>, flags_mode: Expr, src_values: Expr>, ) -> Expr> { @@ -242,15 +245,20 @@ fn logical( } #[hdl_module] -pub fn alu_branch(config: PhantomConst, unit_index: usize) { +pub fn alu_branch(config: &CpuConfig, unit_index: usize) { #[hdl] let cd: ClockDomain = m.input(); #[hdl] let unit_to_reg_alloc: UnitToRegAlloc< - PhantomConst, - AluBranchMOp<(), DynSize>, + AluBranchMOp, DynSize>, (), - > = m.output(UnitToRegAlloc[config][AluBranchMOp[()][PRegNumWidth[config]]][()]); + DynSize, + DynSize, + DynSize, + > = m.output(config.unit_to_reg_alloc( + AluBranchMOp[config.unit_out_reg_num()][config.p_reg_num_width()], + (), + )); #[hdl] let global_state: GlobalState = m.input(); @@ -271,9 +279,13 @@ pub fn alu_branch(config: PhantomConst, unit_index: usize) { #[hdl] if let HdlSome(execute_start) = ReadyValid::firing_data(unit_base.execute_start) { #[hdl] - let ExecuteStart::<_, _> { insn, src_values } = execute_start; + let ExecuteStart::<_> { + mop, + pc, + src_values, + } = execute_start; #[hdl] - match insn.mop { + match mop { AluBranchMOp::<_, _>::AddSub(mop) => connect( unit_base.execute_end, HdlSome( @@ -281,11 +293,10 @@ pub fn alu_branch(config: PhantomConst, unit_index: usize) { ExecuteEnd::<_, _> { unit_output: #[hdl] UnitOutput::<_, _> { - dest: insn.dest, - retire_queue_index: insn.retire_queue_index, + which: MOpTrait::dest_reg(mop), result: UnitResult[()].Completed(add_sub( mop, - insn.pc, + pc, global_state.flags_mode, src_values, )), @@ -300,11 +311,10 @@ pub fn alu_branch(config: PhantomConst, unit_index: usize) { ExecuteEnd::<_, _> { unit_output: #[hdl] UnitOutput::<_, _> { - dest: insn.dest, - retire_queue_index: insn.retire_queue_index, + which: MOpTrait::dest_reg(mop), result: UnitResult[()].Completed(add_sub( mop, - insn.pc, + pc, global_state.flags_mode, src_values, )), @@ -319,8 +329,7 @@ pub fn alu_branch(config: PhantomConst, unit_index: usize) { ExecuteEnd::<_, _> { unit_output: #[hdl] UnitOutput::<_, _> { - dest: insn.dest, - retire_queue_index: insn.retire_queue_index, + which: MOpTrait::dest_reg(mop), result: UnitResult[()].Completed(logical( mop, global_state.flags_mode, @@ -336,14 +345,14 @@ pub fn alu_branch(config: PhantomConst, unit_index: usize) { #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct AluBranch { - config: PhantomConst, + config: Interned, module: Interned>, } impl AluBranch { - pub fn new(config: PhantomConst, unit_index: usize) -> Self { + pub fn new(config: &CpuConfig, unit_index: usize) -> Self { Self { - config, + config: config.intern(), module: alu_branch(config, unit_index), } } @@ -352,7 +361,7 @@ impl AluBranch { impl UnitTrait for AluBranch { type Type = alu_branch; type ExtraOut = (); - type MOp = AluBranchMOp<(), DynSize>; + type MOp = AluBranchMOp, DynSize>; fn ty(&self) -> Self::Type { self.module.io_ty() @@ -370,7 +379,10 @@ impl UnitTrait for AluBranch { UnitKind::AluBranch } - fn extract_mop(&self, mop: Expr>) -> Expr> { + fn extract_mop( + &self, + mop: Expr, DynSize>>, + ) -> Expr> { UnitMOp::alu_branch_mop(mop) } @@ -381,7 +393,7 @@ impl UnitTrait for AluBranch { fn unit_to_reg_alloc( &self, this: Expr, - ) -> Expr, Self::MOp, Self::ExtraOut>> { + ) -> Expr> { this.unit_to_reg_alloc } diff --git a/crates/cpu/src/unit/unit_base.rs b/crates/cpu/src/unit/unit_base.rs index 141ca90..9a3d0d8 100644 --- a/crates/cpu/src/unit/unit_base.rs +++ b/crates/cpu/src/unit/unit_base.rs @@ -2,31 +2,72 @@ // See Notices.txt for copyright information use crate::{ - config::{CpuConfig, CpuConfigType, UnitOutRegNumWidth}, + config::CpuConfig, instruction::{MOpTrait, PRegNum, UnitNum, UnitOutRegNum, COMMON_MOP_SRC_LEN}, register::PRegValue, - unit::{ - RenamedInsnData, RetireQueueIndex, UnitCancelInput, UnitForwardingInfo, UnitOutput, - UnitOutputWrite, UnitToRegAlloc, - }, + unit::{UnitCancelInput, UnitOutput, UnitOutputWrite}, + util::tree_reduce::tree_reduce, }; use fayalite::{ memory::splat_mask, module::{memory_with_loc, wire_with_loc}, prelude::*, ty::StaticType, - util::{prefix_sum::reduce, ready_valid::ReadyValid}, + util::ready_valid::ReadyValid, }; +use std::marker::PhantomData; -#[hdl(no_static)] -pub struct ExecuteStart { - pub insn: RenamedInsnData>, +#[hdl] +pub struct UnitForwardingInfo { + pub unit_output_writes: ArrayType>, UnitCount>, + pub unit_reg_frees: ArrayType>, UnitCount>, + pub _phantom: PhantomData, +} + +#[hdl] +pub struct UnitInput { + pub mop: MOp, + pub pc: UInt<64>, +} + +#[hdl] +pub struct UnitToRegAlloc< + MOp: Type, + ExtraOut: Type, + UnitNumWidth: Size, + OutRegNumWidth: Size, + UnitCount: Size, +> { + #[hdl(flip)] + pub unit_forwarding_info: UnitForwardingInfo, + #[hdl(flip)] + pub input: ReadyValid>, + #[hdl(flip)] + pub cancel_input: HdlOption>, + pub output: HdlOption>, +} + +impl + UnitToRegAlloc +{ + pub fn mop_ty(self) -> MOp { + self.input.data.HdlSome.mop + } + pub fn extra_out_ty(self) -> ExtraOut { + self.output.HdlSome.extra_out_ty() + } +} + +#[hdl] +pub struct ExecuteStart>> { + pub mop: MOp, + pub pc: UInt<64>, pub src_values: Array, } -#[hdl(no_static)] -pub struct ExecuteEnd { - pub unit_output: UnitOutput, +#[hdl] +pub struct ExecuteEnd { + pub unit_output: UnitOutput, } #[hdl] @@ -106,34 +147,27 @@ impl InFlightOpState { } } -#[hdl(no_static)] -struct InFlightOp { +#[hdl] +struct InFlightOp { state: InFlightOpState, - insn: RenamedInsnData>, + mop: MOp, + pc: UInt<64>, src_ready_flags: Array, } -impl InFlightOp { - fn config(self) -> C { - self.insn.retire_queue_index.config - } -} - -#[hdl(no_static)] -struct InFlightOpsSummary { +#[hdl] +struct InFlightOpsSummary { empty_op_index: HdlOption>, ready_op_index: HdlOption>, - ready_for_retire_queue_index: HdlOption>, } -impl InFlightOpsSummary { +impl InFlightOpsSummary { #[hdl] fn new( op_index: usize, op_index_ty: UIntType, - in_flight_op: impl ToExpr>>, + in_flight_op: impl ToExpr>>, ) -> Expr { - let in_flight_op = in_flight_op.to_expr(); let empty_op_index = wire_with_loc( &format!("empty_op_index_{op_index}"), SourceLocation::caller(), @@ -146,73 +180,34 @@ impl InFlightOpsSummary { + let InFlightOp::<_> { state, - insn, + mop: _, + pc: _, src_ready_flags, } = in_flight_op; connect(ready_op_index, HdlOption[op_index_ty].HdlNone()); - let ready_for_retire_queue_index_value = wire_with_loc( - &format!("ready_for_retire_queue_index_value_{op_index}"), - SourceLocation::caller(), - RetireQueueIndex[Expr::ty(in_flight_op).config()], - ); - connect(ready_for_retire_queue_index_value, insn.retire_queue_index); - // TODO: don't subtract one from `.index` when instruction is ready to - // do something at retire time (e.g. non-speculative stores) - // -- that will need a new InFlightOpState variant. - connect_any( - ready_for_retire_queue_index_value.index, - // subtract one -- this instruction isn't ready to retire, - // but the previous one could be - insn.retire_queue_index.index - 1u8, - ); - #[hdl] match state { - InFlightOpState::Ready => { + InFlightOpState::Ready => + { #[hdl] if src_ready_flags.cmp_eq([true; COMMON_MOP_SRC_LEN]) { connect(ready_op_index, HdlSome(op_index.cast_to(op_index_ty))); } - connect( - ready_for_retire_queue_index, - HdlSome(ready_for_retire_queue_index_value), - ); - } - InFlightOpState::CanceledAndRunning => { - // the instruction has been canceled, but is still - // executing so we treat it as if it doesn't exist - // other than making sure the in_flight_op slot - // isn't reused until execution is done. - } - InFlightOpState::Running => { - connect( - ready_for_retire_queue_index, - HdlSome(ready_for_retire_queue_index_value), - ); } + InFlightOpState::CanceledAndRunning | InFlightOpState::Running => {} } } else { connect(empty_op_index, HdlSome(op_index.cast_to(op_index_ty))); } #[hdl] - InFlightOpsSummary::<_, _> { + InFlightOpsSummary::<_> { empty_op_index, ready_op_index, - ready_for_retire_queue_index, } } #[hdl] @@ -220,60 +215,22 @@ impl InFlightOpsSummary { + InFlightOpsSummary::<_> { empty_op_index: HdlOption::or(l.empty_op_index, r.empty_op_index), ready_op_index: HdlOption::or(l.ready_op_index, r.ready_op_index), - ready_for_retire_queue_index: combine_ready_for_retire_queue_index, } } } -impl InFlightOpsSummary, DynSize> { +impl InFlightOpsSummary { fn summarize( - in_flight_ops: impl ToExpr< - Type = ArrayType, MOp>>, MaxInFlight>, - >, + in_flight_ops: impl ToExpr>, MaxInFlight>>, ) -> Expr { let in_flight_ops = in_flight_ops.to_expr(); let max_in_flight = Expr::ty(in_flight_ops).len(); let index_range = 0..max_in_flight; let index_ty = UInt::range(index_range.clone()); - reduce( + tree_reduce( index_range.map(|i| Self::new(i, index_ty, in_flight_ops[i])), Self::combine, ) @@ -282,8 +239,11 @@ impl InFlightOpsSummary, DynSize> { } #[hdl_module] -pub fn unit_base, ExtraOut: Type>( - config: PhantomConst, +pub fn unit_base< + MOp: Type + MOpTrait, SrcRegWidth = DynSize>, + ExtraOut: Type, +>( + config: &CpuConfig, unit_index: usize, mop_ty: MOp, extra_out_ty: ExtraOut, @@ -291,19 +251,18 @@ pub fn unit_base, Extr #[hdl] let cd: ClockDomain = m.input(); #[hdl] - let unit_to_reg_alloc: UnitToRegAlloc, MOp, ExtraOut> = - m.output(UnitToRegAlloc[config][mop_ty][extra_out_ty]); + let unit_to_reg_alloc: UnitToRegAlloc = + m.output(config.unit_to_reg_alloc(mop_ty, extra_out_ty)); #[hdl] - let execute_start: ReadyValid, MOp>> = - m.output(ReadyValid[ExecuteStart[config][mop_ty]]); + let execute_start: ReadyValid> = m.output(ReadyValid[ExecuteStart[mop_ty]]); #[hdl] - let execute_end: HdlOption, ExtraOut>> = - m.input(HdlOption[ExecuteEnd[config][extra_out_ty]]); + let execute_end: HdlOption> = + m.input(HdlOption[ExecuteEnd[config.out_reg_num_width][extra_out_ty]]); connect(execute_start.data, Expr::ty(execute_start).data.HdlNone()); - let max_in_flight = config.get().unit_max_in_flight(unit_index).get(); - let in_flight_op_ty = InFlightOp[config][mop_ty]; + let max_in_flight = config.unit_max_in_flight(unit_index).get(); + let in_flight_op_ty = InFlightOp[mop_ty]; #[hdl] let in_flight_ops = reg_builder() .clock_domain(cd) @@ -319,21 +278,17 @@ pub fn unit_base, Extr HdlOption::is_some(in_flight_ops_summary.empty_op_index), ); - connect( - unit_to_reg_alloc.ready_for_retire_queue_index, - in_flight_ops_summary.ready_for_retire_queue_index, - ); - #[hdl] - let UnitForwardingInfo::<_> { + let UnitForwardingInfo::<_, _, _> { unit_output_writes, unit_reg_frees, + _phantom: _, } = unit_to_reg_alloc.unit_forwarding_info; #[hdl] let read_src_regs = wire(mop_ty.src_regs_ty()); connect( read_src_regs, - repeat(PRegNum[config].const_zero().cast_to_bits(), ConstUsize), + repeat(config.p_reg_num().const_zero().cast_to_bits(), ConstUsize), ); #[hdl] let read_src_values = wire(); @@ -342,7 +297,7 @@ pub fn unit_base, Extr let input_src_regs = wire(mop_ty.src_regs_ty()); connect( input_src_regs, - repeat(PRegNum[config].const_zero().cast_to_bits(), ConstUsize), + repeat(config.p_reg_num().const_zero().cast_to_bits(), ConstUsize), ); #[hdl] let input_src_regs_valid = wire(); @@ -354,7 +309,7 @@ pub fn unit_base, Extr Bool, SourceLocation::caller(), ); - mem.depth(1 << UnitOutRegNumWidth[config]); + mem.depth(1 << config.out_reg_num_width); mem }) .collect(); @@ -364,11 +319,11 @@ pub fn unit_base, Extr PRegValue, SourceLocation::caller(), ); - unit_output_regs.depth(1 << UnitOutRegNumWidth[config]); + unit_output_regs.depth(1 << config.out_reg_num_width); for src_index in 0..COMMON_MOP_SRC_LEN { let read_port = unit_output_regs.new_read_port(); - let p_reg_num = read_src_regs[src_index].cast_bits_to(PRegNum[config]); + let p_reg_num = read_src_regs[src_index].cast_bits_to(config.p_reg_num()); connect_any(read_port.addr, p_reg_num.unit_out_reg.value); connect(read_port.en, false); connect(read_port.clk, cd.clk); @@ -381,7 +336,7 @@ pub fn unit_base, Extr for src_index in 0..COMMON_MOP_SRC_LEN { let read_port = unit_output_regs_valid[unit_index].new_read_port(); - let p_reg_num = input_src_regs[src_index].cast_bits_to(PRegNum[config]); + let p_reg_num = input_src_regs[src_index].cast_bits_to(config.p_reg_num()); connect_any(read_port.addr, p_reg_num.unit_out_reg.value); connect(read_port.en, false); connect(read_port.clk, cd.clk); @@ -406,15 +361,15 @@ pub fn unit_base, Extr connect(ready_write_port.mask, true); #[hdl] if let HdlSome(unit_output_write) = unit_output_writes[unit_index] { - connect_any(write_port.addr, unit_output_write.dest.value); + connect_any(write_port.addr, unit_output_write.which.value); connect(write_port.data, unit_output_write.value); connect(write_port.en, true); - connect_any(ready_write_port.addr, unit_output_write.dest.value); + connect_any(ready_write_port.addr, unit_output_write.which.value); connect(ready_write_port.en, true); let p_reg_num = #[hdl] - PRegNum::<_> { - unit_num: UnitNum[config].from_index(unit_index), - unit_out_reg: unit_output_write.dest, + PRegNum::<_, _> { + unit_num: config.unit_num().from_index(unit_index), + unit_out_reg: unit_output_write.which, }; for src_index in 0..COMMON_MOP_SRC_LEN { #[hdl] @@ -444,8 +399,9 @@ pub fn unit_base, Extr execute_start.data, HdlSome( #[hdl] - ExecuteStart::<_, _> { - insn: in_flight_op.insn, + ExecuteStart::<_> { + mop: in_flight_op.mop, + pc: in_flight_op.pc, src_values: read_src_values, }, ), @@ -464,17 +420,12 @@ pub fn unit_base, Extr #[hdl] if let HdlSome(input) = ReadyValid::firing_data(unit_to_reg_alloc.input) { #[hdl] - let RenamedInsnData::<_, _, _> { - retire_queue_index, - pc: _, - dest: _, - mop, - } = input; + let UnitInput::<_> { mop, pc } = input; #[hdl] let input_mop_src_regs = wire(mop_ty.src_regs_ty()); connect( input_mop_src_regs, - repeat(PRegNum[config].const_zero().cast_to_bits(), ConstUsize), + repeat(config.p_reg_num().const_zero().cast_to_bits(), ConstUsize), ); MOp::connect_src_regs(mop, input_mop_src_regs); let src_ready_flags = wire_with_loc( @@ -485,24 +436,20 @@ pub fn unit_base, Extr connect(src_ready_flags, input_src_regs_valid); connect(input_src_regs, input_mop_src_regs); #[hdl] - let input_is_canceled = wire(); - connect(input_is_canceled, false); - #[hdl] - if let HdlSome(cancel_input) = unit_to_reg_alloc.cancel_input { - connect( - input_is_canceled, - UnitCancelInput::is_canceled(cancel_input, retire_queue_index), - ); - } - #[hdl] - if !input_is_canceled { + if unit_to_reg_alloc.cancel_input.cmp_ne(HdlSome( + #[hdl] + UnitCancelInput::<_> { + which: MOp::dest_reg(mop), + }, + )) { connect( input_in_flight_op, HdlSome( #[hdl] - InFlightOp::<_, _> { + InFlightOp::<_> { state: InFlightOpState.Ready(), - insn: input, + mop, + pc, src_ready_flags, }, ), @@ -536,11 +483,13 @@ pub fn unit_base, Extr #[hdl] if let HdlSome(in_flight_op) = in_flight_ops[in_flight_op_index] { #[hdl] - let InFlightOp::<_, _> { + let InFlightOp::<_> { state, - insn, + mop, + pc, src_ready_flags, } = in_flight_op; + let which = MOp::dest_reg(mop); let src_regs = wire_with_loc( &format!("in_flight_op_src_regs_{in_flight_op_index}"), SourceLocation::caller(), @@ -548,9 +497,9 @@ pub fn unit_base, Extr ); connect( src_regs, - repeat(PRegNum[config].const_zero().cast_to_bits(), ConstUsize), + repeat(config.p_reg_num().const_zero().cast_to_bits(), ConstUsize), ); - MOp::connect_src_regs(insn.mop, src_regs); + MOp::connect_src_regs(mop, src_regs); #[hdl] if in_flight_ops_summary.ready_op_index.cmp_eq(HdlSome( @@ -568,12 +517,12 @@ pub fn unit_base, Extr if let HdlSome(unit_output_write) = unit_output_writes[unit_index] { #[hdl] let UnitOutputWrite::<_> { - dest: unit_out_reg, + which: unit_out_reg, value: _, } = unit_output_write; let p_reg_num = #[hdl] - PRegNum::<_> { - unit_num: UnitNum[config].from_index(unit_index), + PRegNum::<_, _> { + unit_num: config.unit_num().from_index(unit_index), unit_out_reg, }; for src_index in 0..COMMON_MOP_SRC_LEN { @@ -588,21 +537,20 @@ pub fn unit_base, Extr } } - connect(in_flight_op_canceling[in_flight_op_index], false); - #[hdl] - if let HdlSome(cancel_input) = unit_to_reg_alloc.cancel_input { - connect( - in_flight_op_canceling[in_flight_op_index], - UnitCancelInput::is_canceled(cancel_input, insn.retire_queue_index), - ); - } + connect( + in_flight_op_canceling[in_flight_op_index], + unit_to_reg_alloc.cancel_input.cmp_eq(HdlSome( + #[hdl] + UnitCancelInput::<_> { which }, + )), + ); #[hdl] if let HdlSome(execute_end) = execute_end { #[hdl] let ExecuteEnd::<_, _> { unit_output } = execute_end; #[hdl] - if insn.dest.cmp_eq(unit_output.dest) { + if which.cmp_eq(unit_output.which) { connect(in_flight_op_execute_ending[in_flight_op_index], true); #[hdl] if !in_flight_op_canceling[in_flight_op_index] { @@ -619,7 +567,7 @@ pub fn unit_base, Extr #[hdl] if let HdlSome(execute_start) = ReadyValid::firing_data(execute_start) { #[hdl] - if insn.dest.cmp_eq(execute_start.insn.dest) { + if which.cmp_eq(MOp::dest_reg(execute_start.mop)) { connect(in_flight_op_execute_starting[in_flight_op_index], true); } } @@ -646,9 +594,10 @@ pub fn unit_base, Extr in_flight_ops[in_flight_op_index], HdlSome( #[hdl] - InFlightOp::<_, _> { + InFlightOp::<_> { state, - insn, + mop, + pc, src_ready_flags: in_flight_op_next_src_ready_flags[in_flight_op_index], }, ), diff --git a/crates/cpu/src/util.rs b/crates/cpu/src/util.rs index f57003a..0b53274 100644 --- a/crates/cpu/src/util.rs +++ b/crates/cpu/src/util.rs @@ -2,6 +2,7 @@ // See Notices.txt for copyright information pub mod array_vec; +pub mod tree_reduce; pub(crate) const fn range_u32_len(range: &std::ops::Range) -> usize { let retval = range.end.saturating_sub(range.start); @@ -24,16 +25,3 @@ pub(crate) const fn range_u32_nth_or_panic(range: &std::ops::Range, index: panic!("index out of range") } } - -pub(crate) const fn range_intersection( - a: &std::ops::Range, - b: &std::ops::Range, -) -> Option> { - let start = if a.start > b.start { a.start } else { b.start }; - let end = if a.end < b.end { a.end } else { b.end }; - if start < end { - Some(start..end) - } else { - None - } -} diff --git a/crates/cpu/src/util/array_vec.rs b/crates/cpu/src/util/array_vec.rs index be256b4..761f53f 100644 --- a/crates/cpu/src/util/array_vec.rs +++ b/crates/cpu/src/util/array_vec.rs @@ -2,11 +2,8 @@ // See Notices.txt for copyright information use fayalite::{ - expr::{ - ops::{ExprCastTo, ExprIndex, ExprPartialEq, ExprPartialOrd}, - ToLiteralBits, - }, - int::{IntType, SizeType}, + expr::ops::{ExprCastTo, ExprIndex, ExprPartialEq, ExprPartialOrd}, + int::SizeType, intern::{Intern, Interned}, prelude::*, ty::{MatchVariantWithoutScope, StaticType, TypeProperties}, @@ -252,29 +249,6 @@ impl ArrayVec { }); array_vec_as_array_of_options } - #[hdl] - pub fn get>( - this: impl ToExpr, - index: impl ToExpr, - ) -> Expr> { - let this = this.to_expr(); - let index = Expr::as_dyn_int(index.to_expr()); - let never_in_bounds = index.cmp_ge(Expr::ty(this).capacity()); - if let Ok(never_in_bounds) = never_in_bounds.to_literal_bits() { - if never_in_bounds[0] { - // avoid error from out-of-bounds constant index - return HdlOption[Expr::ty(this).element()].HdlNone(); - } - } - #[hdl] - let array_vec_get = wire(HdlOption[Expr::ty(this).element()]); - connect(array_vec_get, Expr::ty(array_vec_get).HdlNone()); - #[hdl] - if index.cmp_lt(Length::as_uint(Self::len(this))) { - connect(array_vec_get, HdlSome(this.elements[index])); - } - array_vec_get - } } impl ExprIndex for ArrayVec @@ -289,35 +263,3 @@ where as ExprIndex>::expr_index(&this.elements, index) } } - -#[hdl] -pub struct ReadyValidArray { - pub data: ArrayVec, - #[hdl(flip)] - pub ready: Length, -} - -impl ReadyValidArray { - #[hdl] - pub fn firing_len(this: impl ToExpr) -> Expr> { - let this = this.to_expr(); - assert_eq!(Expr::ty(this).data.len_ty(), Expr::ty(this).ready); - #[hdl] - let firing_len = wire(Expr::ty(this).data.len); - connect(firing_len, this.data.len); - #[hdl] - if this.data.len.cmp_gt(this.ready) { - connect(firing_len, this.ready); - } - firing_len - } - #[hdl] - pub fn firing_data(this: impl ToExpr) -> Expr> { - let this = this.to_expr(); - #[hdl] - let firing_data = wire(Expr::ty(this).data); - connect(firing_data, this.data); - connect(firing_data.len, Self::firing_len(this)); - firing_data - } -} diff --git a/crates/cpu/src/util/tree_reduce.rs b/crates/cpu/src/util/tree_reduce.rs new file mode 100644 index 0000000..c8d12f7 --- /dev/null +++ b/crates/cpu/src/util/tree_reduce.rs @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub enum TreeReduceOp { + Input, + Reduce, +} + +#[derive(Copy, Clone, Debug)] +struct Entry { + start: usize, + depth: u32, +} + +#[derive(Clone, Debug)] +pub struct TreeReduceOps { + len: usize, + stack: Vec, +} + +impl TreeReduceOps { + pub fn new(len: usize) -> Self { + TreeReduceOps { + len, + stack: Vec::new(), + } + } +} + +impl Iterator for TreeReduceOps { + type Item = TreeReduceOp; + fn next(&mut self) -> Option { + match *self.stack { + [] if self.len != 0 => { + self.stack.push(Entry { start: 0, depth: 0 }); + Some(TreeReduceOp::Input) + } + [.., ref mut second_last, last] if second_last.depth == last.depth => { + second_last.depth += 1; + self.stack.pop(); + Some(TreeReduceOp::Reduce) + } + [.., last] if self.len - last.start > 1 << last.depth => { + let start = last.start + (1 << last.depth); + self.stack.push(Entry { start, depth: 0 }); + Some(TreeReduceOp::Input) + } + [.., ref mut second_last, _] => { + second_last.depth += 1; + self.stack.pop(); + Some(TreeReduceOp::Reduce) + } + _ => None, + } + } +} + +#[track_caller] +pub fn tree_reduce_with_state( + iter: impl IntoIterator, + state: &mut S, + mut input: impl FnMut(&mut S, I) -> R, + mut reduce: impl FnMut(&mut S, R, R) -> R, +) -> Option { + let mut stack = Vec::new(); + let mut iter = iter.into_iter(); + for op in TreeReduceOps::new(iter.len()) { + match op { + TreeReduceOp::Input => stack.push(input( + state, + iter.next().expect("inconsistent iterator len() and next()"), + )), + TreeReduceOp::Reduce => { + let Some(r) = stack.pop() else { + unreachable!(); + }; + let Some(l) = stack.pop() else { + unreachable!(); + }; + stack.push(reduce(state, l, r)); + } + } + } + stack.pop() +} + +pub fn tree_reduce( + iter: impl IntoIterator, + mut reduce: impl FnMut(T, T) -> T, +) -> Option { + tree_reduce_with_state(iter, &mut (), |_, v| v, move |_, l, r| reduce(l, r)) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::ops::Range; + + fn recursive_tree_reduce(range: Range, ops: &mut Vec) { + if range.len() == 1 { + ops.push(TreeReduceOp::Input); + return; + } + if range.is_empty() { + return; + } + let pow2_len = range.len().next_power_of_two(); + let split = range.start + pow2_len / 2; + recursive_tree_reduce(range.start..split, ops); + recursive_tree_reduce(split..range.end, ops); + ops.push(TreeReduceOp::Reduce); + } + + #[test] + fn test_tree_reduce() { + const EXPECTED: &'static [&'static [TreeReduceOp]] = { + use TreeReduceOp::{Input as I, Reduce as R}; + &[ + &[], + &[I], + &[I, I, R], + &[I, I, R, I, R], + &[I, I, R, I, I, R, R], + &[I, I, R, I, I, R, R, I, R], + &[I, I, R, I, I, R, R, I, I, R, R], + &[I, I, R, I, I, R, R, I, I, R, I, R, R], + &[I, I, R, I, I, R, R, I, I, R, I, I, R, R, R], + ] + }; + for len in 0..64 { + let mut expected = vec![]; + recursive_tree_reduce(0..len, &mut expected); + if let Some(&expected2) = EXPECTED.get(len) { + assert_eq!(*expected, *expected2, "len={len}"); + } + assert_eq!( + TreeReduceOps::new(len).collect::>(), + expected, + "len={len}" + ); + let seq: Vec<_> = (0..len).collect(); + assert_eq!( + seq, + tree_reduce(seq.iter().map(|&v| vec![v]), |mut l, r| { + l.extend_from_slice(&r); + l + }) + .unwrap_or_default() + ); + } + } +} diff --git a/crates/cpu/tests/reg_alloc.rs b/crates/cpu/tests/reg_alloc.rs index 0484aff..beac954 100644 --- a/crates/cpu/tests/reg_alloc.rs +++ b/crates/cpu/tests/reg_alloc.rs @@ -1,8 +1,6 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information -#![cfg(todo)] - use cpu::{ config::{CpuConfig, UnitConfig}, instruction::{AddSubMOp, LogicalMOp, MOp, MOpDestReg, MOpRegNum, OutputIntegerMode}, diff --git a/crates/name_mangling_serde/Cargo.toml b/crates/name_mangling_serde/Cargo.toml deleted file mode 100644 index c69574d..0000000 --- a/crates/name_mangling_serde/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -# SPDX-License-Identifier: LGPL-3.0-or-later -# See Notices.txt for copyright information -[package] -name = "name_mangling_serde" -description = "serde serializer/deserializer for name mangling" -workspace = "../.." -readme = "README.md" -publish = false -edition.workspace = true -license.workspace = true -repository.workspace = true -rust-version.workspace = true -version.workspace = true - -[dependencies] -serde.workspace = true -serde_json.workspace = true - -[lints.rust] -unexpected_cfgs = { level = "warn", check-cfg = ['cfg(todo)'] } diff --git a/crates/name_mangling_serde/LICENSE.md b/crates/name_mangling_serde/LICENSE.md deleted file mode 120000 index f0608a6..0000000 --- a/crates/name_mangling_serde/LICENSE.md +++ /dev/null @@ -1 +0,0 @@ -../../LICENSE.md \ No newline at end of file diff --git a/crates/name_mangling_serde/Notices.txt b/crates/name_mangling_serde/Notices.txt deleted file mode 120000 index 9f3a306..0000000 --- a/crates/name_mangling_serde/Notices.txt +++ /dev/null @@ -1 +0,0 @@ -../../Notices.txt \ No newline at end of file diff --git a/crates/name_mangling_serde/src/lib.rs b/crates/name_mangling_serde/src/lib.rs deleted file mode 100644 index 6667da7..0000000 --- a/crates/name_mangling_serde/src/lib.rs +++ /dev/null @@ -1,470 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-or-later -// See Notices.txt for copyright information - -use serde::{de::DeserializeOwned, Serialize}; -use serde_json::{Map, Number, Value}; -use std::{ - fmt::{self, Write}, - num::ParseIntError, -}; - -macro_rules! byte_enum { - ( - #[repr(u8)] - $(#[$meta:meta])* - $vis:vis enum $enum:ident { - $($Variant:ident = $value:expr,)* - } - ) => { - #[repr(u8)] - $(#[$meta])* - $vis enum $enum { - $($Variant = $value,)* - } - - impl $enum { - $vis fn new(v: u8) -> Option { - struct Values; - #[allow(non_upper_case_globals)] - impl Values { - $(const $Variant: u8 = $enum::$Variant as u8;)* - } - match v { - $(Values::$Variant => Some(Self::$Variant),)* - _ => None, - } - } - #[allow(dead_code)] - $vis fn as_char(self) -> char { - const { - $(assert!((Self::$Variant as u8).is_ascii());)* - }; - self as u8 as char - } - } - }; -} - -macro_rules! string_escapes { - ( - $key_vis:vis enum $StringEscapeKey:ident {} - $value_vis:vis enum $StringEscapeValue:ident { - $( - #[key = $key:expr] - $Variant:ident = $value:expr, - )* - } - ) => { - byte_enum! { - #[repr(u8)] - #[derive(Clone, Copy, Debug)] - $key_vis enum $StringEscapeKey { - $($Variant = $key,)* - } - } - - byte_enum! { - #[repr(u8)] - #[derive(Clone, Copy, Debug)] - $value_vis enum $StringEscapeValue { - $($Variant = $value,)* - } - } - - impl From<$StringEscapeKey> for $StringEscapeValue { - fn from(v: $StringEscapeKey) -> Self { - match v { - $($StringEscapeKey::$Variant => Self::$Variant,)* - } - } - } - - impl From<$StringEscapeValue> for $StringEscapeKey { - fn from(v: $StringEscapeValue) -> Self { - match v { - $($StringEscapeValue::$Variant => Self::$Variant,)* - } - } - } - }; -} - -string_escapes! { - enum StringEscapeKey {} - enum StringEscapeValue { - #[key = b's'] - Space = b' ', - #[key = b't'] - Tab = b'\t', - #[key = b'r'] - CR = b'\r', - #[key = b'n'] - NewLine = b'\n', - #[key = b'_'] - Underline = b'_', - } -} - -fn json_string_to_name_part(value: &str, out: &mut String) { - out.push(ValuePrefix::String.as_char()); - write!(out, "{}_", value.len()).unwrap(); - for b in value.bytes() { - if let Some(v) = StringEscapeValue::new(b) { - out.push('_'); - out.push(StringEscapeKey::from(v).as_char()); - } else if b.is_ascii_alphanumeric() { - out.push(b as char); - } else { - write!(out, "_{b:02x}").unwrap() - } - } -} - -byte_enum! { - #[repr(u8)] - #[derive(Clone, Copy, Debug)] - enum ValuePrefix { - Null = b'z', - False = b'f', - True = b't', - Number = b'n', - String = b's', - Array = b'a', - Object = b'o', - } -} - -fn json_value_to_name_part(value: &Value, out: &mut String) { - match value { - Value::Null => out.push(ValuePrefix::Null.as_char()), - Value::Bool(false) => out.push(ValuePrefix::False.as_char()), - Value::Bool(true) => out.push(ValuePrefix::True.as_char()), - Value::Number(number) => { - out.push(ValuePrefix::Number.as_char()); - let start = out.len(); - write!(out, "{number}").unwrap(); - for i in start..out.len() { - out.replace_range( - i..=i, - match out.as_bytes()[i] { - b'0'..=b'9' => continue, - b'+' => "", - b'-' => "n", - b'.' => "p", - b'e' | b'E' => "e", - _ => unreachable!("invalid character in JSON number"), - }, - ); - } - } - Value::String(string) => json_string_to_name_part(string, out), - Value::Array(array) => { - out.push(ValuePrefix::Array.as_char()); - write!(out, "{}", array.len()).unwrap(); - for element in array { - json_value_to_name_part(element, out); - } - } - Value::Object(object) => { - out.push(ValuePrefix::Object.as_char()); - write!(out, "{}", object.len()).unwrap(); - for (k, v) in object { - json_string_to_name_part(k, out); - json_value_to_name_part(v, out); - } - } - } -} - -pub const NAME_PREFIX: &str = "__HDL"; - -pub fn json_value_to_name(value: &Value) -> String { - let mut retval = NAME_PREFIX.into(); - json_value_to_name_part(value, &mut retval); - retval -} - -#[derive(Debug)] -pub enum Error { - Serde(serde_json::Error), - NameDoesNotStartWithKnownPrefix, - UnknownValuePrefix, - MissingValuePrefix, - InvalidLength(ParseIntError), - TrailingCharacters, - KeyMustBeAString, - StringMissingUnderline, - StringTruncated, - InvalidEscape, - InvalidString, -} - -impl From for Error { - fn from(value: serde_json::Error) -> Self { - Self::Serde(value) - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Serde(e) => e.fmt(f), - Self::NameDoesNotStartWithKnownPrefix => { - f.write_str("name does not start with the known prefix") - } - Self::UnknownValuePrefix => f.write_str("unknown value prefix"), - Self::MissingValuePrefix => f.write_str("missing value prefix"), - Self::InvalidLength(_) => f.write_str("invalid length"), - Self::TrailingCharacters => f.write_str("trailing characters"), - Self::KeyMustBeAString => f.write_str("key must be a string"), - Self::StringMissingUnderline => f.write_str("string missing `_` after length"), - Self::StringTruncated => f.write_str("string truncated"), - Self::InvalidEscape => f.write_str("invalid escape"), - Self::InvalidString => f.write_str("invalid string"), - } - } -} - -impl std::error::Error for Error { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - Self::Serde(e) => e.source(), - Self::NameDoesNotStartWithKnownPrefix => None, - Self::UnknownValuePrefix => None, - Self::MissingValuePrefix => None, - Self::InvalidLength(e) => Some(e), - Self::TrailingCharacters => None, - Self::KeyMustBeAString => None, - Self::StringMissingUnderline => None, - Self::StringTruncated => None, - Self::InvalidEscape => None, - Self::InvalidString => None, - } - } -} - -struct NameParser<'a> { - name_part: &'a str, - number_buf: String, -} - -impl NameParser<'_> { - fn parse_len(&mut self) -> Result { - let len_end = self - .name_part - .bytes() - .position(|b| !b.is_ascii_digit()) - .unwrap_or(self.name_part.len()); - let (len, rest) = self.name_part.split_at(len_end); - self.name_part = rest; - len.parse().map_err(Error::InvalidLength) - } - fn parse_string_without_prefix(&mut self) -> Result { - let len = self.parse_len()?; - let Some(rest) = self.name_part.strip_prefix("_") else { - return Err(Error::StringMissingUnderline); - }; - self.name_part = rest; - let mut bytes = Vec::new(); - for _ in 0..len { - let b = self - .name_part - .bytes() - .next() - .ok_or(Error::StringTruncated)?; - if b.is_ascii_alphanumeric() { - bytes.push(b); - self.name_part = &self.name_part[1..]; - } else if b == b'_' { - self.name_part = &self.name_part[1..]; - let escape = self.name_part.bytes().next().ok_or(Error::InvalidEscape)?; - self.name_part = &self.name_part[1..]; - if let Some(high) = (escape as char).to_digit(16) { - let low = self - .name_part - .bytes() - .next() - .ok_or(Error::StringTruncated)?; - let low = (low as char).to_digit(16).ok_or(Error::InvalidString)?; - self.name_part = &self.name_part[1..]; - bytes.push((high * 16 + low) as u8); - } else { - let escape = StringEscapeKey::new(escape).ok_or(Error::InvalidEscape)?; - bytes.push(StringEscapeValue::from(escape) as u8); - } - } else if let Some(high) = (b as char).to_digit(16) { - self.name_part = &self.name_part[1..]; - let low = self - .name_part - .bytes() - .next() - .ok_or(Error::StringTruncated)?; - let low = (low as char).to_digit(16).ok_or(Error::InvalidString)?; - self.name_part = &self.name_part[1..]; - bytes.push((high * 16 + low) as u8); - } else { - return Err(Error::InvalidString); - } - } - String::from_utf8(bytes).map_err(|_| Error::InvalidString) - } - fn parse_string(&mut self) -> Result { - if let ValuePrefix::String = self.parse_value_prefix()? { - self.parse_string_without_prefix() - } else { - Err(Error::KeyMustBeAString) - } - } - fn parse_number_without_prefix(&mut self) -> Result { - let mut bytes = self.name_part.as_bytes().iter(); - self.number_buf.clear(); - if let Some(b'n') = bytes.clone().next() { - bytes.next(); - self.number_buf.push('-'); - } - while let Some(&b @ b'0'..=b'9') = bytes.clone().next() { - bytes.next(); - self.number_buf.push(b as char); - } - if let Some(b'p') = bytes.clone().next() { - bytes.next(); - self.number_buf.push('.'); - while let Some(&b @ b'0'..=b'9') = bytes.clone().next() { - bytes.next(); - self.number_buf.push(b as char); - } - } - if let Some(b'e') = bytes.clone().next() { - bytes.next(); - self.number_buf.push('e'); - if let Some(b'n') = bytes.clone().next() { - bytes.next(); - self.number_buf.push('-'); - } - while let Some(&b @ b'0'..=b'9') = bytes.clone().next() { - bytes.next(); - self.number_buf.push(b as char); - } - } - self.name_part = &self.name_part[self.name_part.len() - bytes.len()..]; - Ok(self.number_buf.parse()?) - } - fn parse_value_prefix(&mut self) -> Result { - let value_prefix = self - .name_part - .bytes() - .next() - .ok_or(Error::MissingValuePrefix)?; - let value_prefix = ValuePrefix::new(value_prefix).ok_or(Error::UnknownValuePrefix)?; - self.name_part = &self.name_part[1..]; - Ok(value_prefix) - } - fn parse_value(&mut self) -> Result { - Ok(match self.parse_value_prefix()? { - ValuePrefix::Null => Value::Null, - ValuePrefix::False => Value::Bool(false), - ValuePrefix::True => Value::Bool(true), - ValuePrefix::Number => Value::Number(self.parse_number_without_prefix()?), - ValuePrefix::String => Value::String(self.parse_string_without_prefix()?), - ValuePrefix::Array => { - let len = self.parse_len()?; - let mut array = Vec::new(); - for _ in 0..len { - array.push(self.parse_value()?); - } - Value::Array(array) - } - ValuePrefix::Object => { - let len = self.parse_len()?; - let mut object = Map::new(); - for _ in 0..len { - let key = self.parse_string()?; - let value = self.parse_value()?; - object.insert(key, value); - } - Value::Object(object) - } - }) - } -} - -pub fn name_to_json_value(name: &str) -> Result { - let Some(name_part) = name.strip_prefix(NAME_PREFIX) else { - return Err(Error::NameDoesNotStartWithKnownPrefix); - }; - let mut parser = NameParser { - name_part, - number_buf: String::new(), - }; - let retval = parser.parse_value()?; - if !parser.name_part.is_empty() { - Err(Error::TrailingCharacters) - } else { - Ok(retval) - } -} - -pub fn from_name(name: &str) -> Result { - Ok(serde_json::from_value(name_to_json_value(name)?)?) -} - -pub fn to_name(value: T) -> Result { - Ok(json_value_to_name(&serde_json::to_value(value)?)) -} - -#[cfg(test)] -mod tests { - use serde_json::json; - - use super::*; - - #[test] - fn test_from_to_name() { - #[track_caller] - fn check_from_to_name(value: Value, name: &str) { - assert_eq!(name, json_value_to_name(&value)); - assert_eq!( - Ok(value), - name_to_json_value(name).map_err(|e| e.to_string()) - ); - } - - check_from_to_name(json! { null }, "__HDLz"); - check_from_to_name(json! { false }, "__HDLf"); - check_from_to_name(json! { true }, "__HDLt"); - check_from_to_name(json! { 0 }, "__HDLn0"); - check_from_to_name(json! { 0.1 }, "__HDLn0p1"); - check_from_to_name(json! { -0.1 }, "__HDLnn0p1"); - check_from_to_name(json! { 1234567 }, "__HDLn1234567"); - check_from_to_name(json! { -1.2345678e-20 }, "__HDLnn1p2345678en20"); - check_from_to_name(json! { -1.2345e300 }, "__HDLnn1p2345e300"); - check_from_to_name(json! { -5 }, "__HDLnn5"); - check_from_to_name(json! { "" }, "__HDLs0_"); - check_from_to_name(json! { "a" }, "__HDLs1_a"); - check_from_to_name(json! { "A" }, "__HDLs1_A"); - check_from_to_name(json! { "z" }, "__HDLs1_z"); - check_from_to_name(json! { "Z" }, "__HDLs1_Z"); - check_from_to_name(json! { "0" }, "__HDLs1_0"); - check_from_to_name(json! { "9" }, "__HDLs1_9"); - check_from_to_name(json! { "_" }, "__HDLs1___"); - check_from_to_name(json! { " " }, "__HDLs1__s"); - check_from_to_name(json! { "\t" }, "__HDLs1__t"); - check_from_to_name(json! { "\r" }, "__HDLs1__r"); - check_from_to_name(json! { "\n" }, "__HDLs1__n"); - check_from_to_name(json! { "\u{25}" }, "__HDLs1__25"); - check_from_to_name(json! { "\u{100}" }, "__HDLs2__c4_80"); - check_from_to_name(json! { "\u{1000}" }, "__HDLs3__e1_80_80"); - check_from_to_name(json! { "\u{10000}" }, "__HDLs4__f0_90_80_80"); - check_from_to_name(json! { "foo" }, "__HDLs3_foo"); - check_from_to_name(json! { { "foo": 123 } }, "__HDLo1s3_foon123"); - check_from_to_name( - json! { { "foo": 123, "bar": null } }, - "__HDLo2s3_foon123s3_barz", - ); - check_from_to_name(json! { [1, 2, 3, 4] }, "__HDLa4n1n2n3n4"); - check_from_to_name( - json! { { "a": [], "b": null, "c": 1234, "d": {} } }, - "__HDLo4s1_aa0s1_bzs1_cn1234s1_do0", - ); - } -}