WIP: splitting reg_alloc
This commit is contained in:
		
							parent
							
								
									9fa959652e
								
							
						
					
					
						commit
						782a44858c
					
				
					 17 changed files with 904 additions and 627 deletions
				
			
		
							
								
								
									
										2
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							|  | @ -207,6 +207,8 @@ name = "cpu" | ||||||
| version = "0.1.0" | version = "0.1.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "fayalite", |  "fayalite", | ||||||
|  |  "name_mangling_serde", | ||||||
|  |  "serde", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
|  |  | ||||||
|  | @ -14,6 +14,7 @@ categories = [] | ||||||
| rust-version = "1.82.0" | rust-version = "1.82.0" | ||||||
| 
 | 
 | ||||||
| [workspace.dependencies] | [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" } | 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 = { version = "1.0.202", features = ["derive"] } | ||||||
| serde_json = { version = "1.0.117", features = ["preserve_order"] } | serde_json = { version = "1.0.117", features = ["preserve_order"] } | ||||||
|  |  | ||||||
|  | @ -16,3 +16,8 @@ version.workspace = true | ||||||
| 
 | 
 | ||||||
| [dependencies] | [dependencies] | ||||||
| fayalite.workspace = true | fayalite.workspace = true | ||||||
|  | serde.workspace = true | ||||||
|  | name_mangling_serde.workspace = true | ||||||
|  | 
 | ||||||
|  | [lints.rust] | ||||||
|  | unexpected_cfgs = { level = "warn", check-cfg = ['cfg(todo)'] } | ||||||
|  |  | ||||||
|  | @ -1,16 +1,17 @@ | ||||||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     instruction::{MOpTrait, PRegNum, RenamedMOp, UnitNum, UnitOutRegNum, CONST_ZERO_UNIT_NUM}, |     instruction::{PRegNum, CONST_ZERO_UNIT_NUM}, | ||||||
|     unit::{ |     unit::UnitKind, | ||||||
|         unit_base::{UnitForwardingInfo, UnitToRegAlloc}, |  | ||||||
|         UnitCancelInput, UnitKind, UnitOutputWrite, |  | ||||||
|     }, |  | ||||||
| }; | }; | ||||||
| use fayalite::prelude::*; | use fayalite::{ | ||||||
|  |     intern::{Intern, Interned}, | ||||||
|  |     prelude::*, | ||||||
|  | }; | ||||||
|  | use serde::{Deserialize, Serialize}; | ||||||
| use std::num::NonZeroUsize; | use std::num::NonZeroUsize; | ||||||
| 
 | 
 | ||||||
| #[derive(Clone, Eq, PartialEq, Hash, Debug)] | #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)] | ||||||
| #[non_exhaustive] | #[non_exhaustive] | ||||||
| pub struct UnitConfig { | pub struct UnitConfig { | ||||||
|     pub kind: UnitKind, |     pub kind: UnitKind, | ||||||
|  | @ -27,15 +28,14 @@ impl UnitConfig { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Clone, Eq, PartialEq, Hash, Debug)] | #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)] | ||||||
| #[non_exhaustive] | #[non_exhaustive] | ||||||
| pub struct CpuConfig { | pub struct CpuConfig { | ||||||
|     pub units: Vec<UnitConfig>, |     pub units: Interned<[UnitConfig]>, | ||||||
|     pub out_reg_num_width: usize, |     pub out_reg_num_width: usize, | ||||||
|     pub fetch_width: NonZeroUsize, |     pub fetch_width: NonZeroUsize, | ||||||
|     /// default value for [`UnitConfig::max_in_flight`]
 |     /// default value for [`UnitConfig::max_in_flight`]
 | ||||||
|     pub default_unit_max_in_flight: NonZeroUsize, |     pub default_unit_max_in_flight: NonZeroUsize, | ||||||
|     pub rob_size: NonZeroUsize, |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl CpuConfig { | impl CpuConfig { | ||||||
|  | @ -52,13 +52,12 @@ impl CpuConfig { | ||||||
|         }; |         }; | ||||||
|         v |         v | ||||||
|     }; |     }; | ||||||
|     pub fn new(units: Vec<UnitConfig>, rob_size: NonZeroUsize) -> Self { |     pub fn new(units: Interned<[UnitConfig]>) -> Self { | ||||||
|         Self { |         Self { | ||||||
|             units, |             units, | ||||||
|             out_reg_num_width: Self::DEFAULT_OUT_REG_NUM_WIDTH, |             out_reg_num_width: Self::DEFAULT_OUT_REG_NUM_WIDTH, | ||||||
|             fetch_width: Self::DEFAULT_FETCH_WIDTH, |             fetch_width: Self::DEFAULT_FETCH_WIDTH, | ||||||
|             default_unit_max_in_flight: Self::DEFAULT_UNIT_MAX_IN_FLIGHT, |             default_unit_max_in_flight: Self::DEFAULT_UNIT_MAX_IN_FLIGHT, | ||||||
|             rob_size, |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     pub fn non_const_unit_nums(&self) -> std::ops::Range<usize> { |     pub fn non_const_unit_nums(&self) -> std::ops::Range<usize> { | ||||||
|  | @ -67,53 +66,83 @@ impl CpuConfig { | ||||||
|     pub fn unit_num_width(&self) -> usize { |     pub fn unit_num_width(&self) -> usize { | ||||||
|         UInt::range(CONST_ZERO_UNIT_NUM..self.non_const_unit_nums().end).width() |         UInt::range(CONST_ZERO_UNIT_NUM..self.non_const_unit_nums().end).width() | ||||||
|     } |     } | ||||||
|     pub fn unit_num(&self) -> UnitNum<DynSize> { |  | ||||||
|         UnitNum[self.unit_num_width()] |  | ||||||
|     } |  | ||||||
|     pub fn unit_out_reg_num(&self) -> UnitOutRegNum<DynSize> { |  | ||||||
|         UnitOutRegNum[self.out_reg_num_width] |  | ||||||
|     } |  | ||||||
|     pub fn p_reg_num(&self) -> PRegNum<DynSize, DynSize> { |  | ||||||
|         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<UnitOutRegNum<DynSize>, DynSize> { |  | ||||||
|         RenamedMOp[self.unit_out_reg_num()][self.p_reg_num_width()] |  | ||||||
|     } |  | ||||||
|     pub fn unit_output_write(&self) -> UnitOutputWrite<DynSize> { |  | ||||||
|         UnitOutputWrite[self.out_reg_num_width] |  | ||||||
|     } |  | ||||||
|     pub fn unit_output_writes(&self) -> Array<HdlOption<UnitOutputWrite<DynSize>>> { |  | ||||||
|         Array[HdlOption[self.unit_output_write()]][self.non_const_unit_nums().len()] |  | ||||||
|     } |  | ||||||
|     pub fn unit_cancel_input(&self) -> UnitCancelInput<DynSize> { |  | ||||||
|         UnitCancelInput[self.out_reg_num_width] |  | ||||||
|     } |  | ||||||
|     pub fn unit_forwarding_info(&self) -> UnitForwardingInfo<DynSize, DynSize, DynSize> { |  | ||||||
|         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 { |     pub fn unit_max_in_flight(&self, unit_index: usize) -> NonZeroUsize { | ||||||
|         self.units[unit_index] |         self.units[unit_index] | ||||||
|             .max_in_flight |             .max_in_flight | ||||||
|             .unwrap_or(self.default_unit_max_in_flight) |             .unwrap_or(self.default_unit_max_in_flight) | ||||||
|     } |     } | ||||||
|     pub fn unit_to_reg_alloc< |     pub fn retire_queue_index_width(&self) -> usize { | ||||||
|         MOp: Type + MOpTrait<DestReg = UnitOutRegNum<DynSize>, SrcRegWidth = DynSize>, |         let max_in_flight: usize = (0..self.units.len()) | ||||||
|         ExtraOut: Type, |             .map(|unit_index| self.unit_max_in_flight(unit_index).get()) | ||||||
|     >( |             .sum(); | ||||||
|         &self, |         2 + max_in_flight.next_power_of_two().ilog2() as usize | ||||||
|         mop_ty: MOp, |  | ||||||
|         extra_out_ty: ExtraOut, |  | ||||||
|     ) -> UnitToRegAlloc<MOp, ExtraOut, DynSize, DynSize, DynSize> { |  | ||||||
|         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<CpuConfig> {} | ||||||
|  | 
 | ||||||
|  | pub trait CpuConfigType: Type + ToExpr<Type = Self> + sealed::Sealed { | ||||||
|  |     fn get(self) -> Interned<CpuConfig>; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl CpuConfigType for PhantomConst<CpuConfig> { | ||||||
|  |     fn get(self) -> Interned<CpuConfig> { | ||||||
|  |         self.get() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub trait Identity<Arg: ?Sized> { | ||||||
|  |     type SelfType: ?Sized; | ||||||
|  |     type ArgType: ?Sized; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T: ?Sized, Arg: ?Sized> Identity<Arg> 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> = <DynSize as Identity<$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<T> = |arg| arg.get().unit_num_width(); | ||||||
|  |     #[without_generics = __UnitOutRegNumWidth_WithoutGenerics] | ||||||
|  |     pub type UnitOutRegNumWidth<T> = |arg| arg.get().out_reg_num_width; | ||||||
|  |     #[without_generics = __PRegNumWidth_WithoutGenerics] | ||||||
|  |     pub type PRegNumWidth<T> = |arg| PRegNum[arg].canonical().bit_width(); | ||||||
|  |     #[without_generics = __RetireQueueIndexWidth_WithoutGenerics] | ||||||
|  |     pub type RetireQueueIndexWidth<T> = |arg| arg.get().retire_queue_index_width(); | ||||||
|  |     #[without_generics = __UnitCount_WithoutGenerics] | ||||||
|  |     pub type UnitCount<T> = |arg| arg.get().non_const_unit_nums().len(); | ||||||
|  |     #[without_generics = __FetchWidth_WithoutGenerics] | ||||||
|  |     pub type FetchWidth<T> = |arg| arg.get().fetch_width.get(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -1,6 +1,10 @@ | ||||||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
| use crate::{unit::UnitMOp, util::range_u32_len}; | use crate::{ | ||||||
|  |     config::{CpuConfigType, UnitNumWidth, UnitOutRegNumWidth}, | ||||||
|  |     unit::UnitMOp, | ||||||
|  |     util::range_u32_len, | ||||||
|  | }; | ||||||
| use fayalite::{ | use fayalite::{ | ||||||
|     expr::ops::{ArrayLiteral, ExprPartialEq}, |     expr::ops::{ArrayLiteral, ExprPartialEq}, | ||||||
|     intern::Interned, |     intern::Interned, | ||||||
|  | @ -805,19 +809,21 @@ common_mop_struct! { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[hdl(cmp_eq)] | #[hdl(cmp_eq, no_static)] | ||||||
| /// there may be more than one unit of a given kind, so UnitNum is not the same as UnitKind.
 | /// 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
 | /// zero is used for built-in constants, such as the zero register
 | ||||||
| pub struct UnitNum<Width: Size> { | pub struct UnitNum<C: Type + CpuConfigType> { | ||||||
|     pub adj_value: UIntType<Width>, |     pub adj_value: UIntType<UnitNumWidth<C>>, | ||||||
|  |     pub config: C, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<Width: Size> UnitNum<Width> { | impl<C: Type + CpuConfigType> UnitNum<C> { | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     pub fn const_zero(self) -> Expr<Self> { |     pub fn const_zero(self) -> Expr<Self> { | ||||||
|         #[hdl] |         #[hdl] | ||||||
|         UnitNum { |         UnitNum { | ||||||
|             adj_value: CONST_ZERO_UNIT_NUM.cast_to(self.adj_value), |             adj_value: CONST_ZERO_UNIT_NUM.cast_to(self.adj_value), | ||||||
|  |             config: self.config, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     #[hdl] |     #[hdl] | ||||||
|  | @ -825,6 +831,7 @@ impl<Width: Size> UnitNum<Width> { | ||||||
|         #[hdl] |         #[hdl] | ||||||
|         UnitNum { |         UnitNum { | ||||||
|             adj_value: (index + 1).cast_to(self.adj_value), |             adj_value: (index + 1).cast_to(self.adj_value), | ||||||
|  |             config: self.config, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     pub fn is_index(expr: impl ToExpr<Type = Self>, index: usize) -> Expr<Bool> { |     pub fn is_index(expr: impl ToExpr<Type = Self>, index: usize) -> Expr<Bool> { | ||||||
|  | @ -835,7 +842,7 @@ impl<Width: Size> UnitNum<Width> { | ||||||
|             .cmp_eq(expr.adj_value) |             .cmp_eq(expr.adj_value) | ||||||
|     } |     } | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     pub fn as_index(expr: impl ToExpr<Type = Self>) -> Expr<HdlOption<UIntType<Width>>> { |     pub fn as_index(expr: impl ToExpr<Type = Self>) -> Expr<HdlOption<UIntType<DynSize>>> { | ||||||
|         let expr = expr.to_expr(); |         let expr = expr.to_expr(); | ||||||
|         #[hdl] |         #[hdl] | ||||||
|         let unit_index = wire(HdlOption[Expr::ty(expr).adj_value]); |         let unit_index = wire(HdlOption[Expr::ty(expr).adj_value]); | ||||||
|  | @ -853,19 +860,20 @@ impl<Width: Size> UnitNum<Width> { | ||||||
| 
 | 
 | ||||||
| pub const CONST_ZERO_UNIT_NUM: usize = 0; | pub const CONST_ZERO_UNIT_NUM: usize = 0; | ||||||
| 
 | 
 | ||||||
| #[hdl(cmp_eq)] | #[hdl(cmp_eq, no_static)] | ||||||
| pub struct UnitOutRegNum<Width: Size> { | pub struct UnitOutRegNum<C: Type + CpuConfigType> { | ||||||
|     pub value: UIntType<Width>, |     pub value: UIntType<UnitOutRegNumWidth<C>>, | ||||||
|  |     pub config: C, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[hdl(cmp_eq)] | #[hdl(cmp_eq, no_static)] | ||||||
| /// Physical Register Number -- registers in the CPU's backend
 | /// Physical Register Number -- registers in the CPU's backend
 | ||||||
| pub struct PRegNum<UnitNumWidth: Size, OutRegNumWidth: Size> { | pub struct PRegNum<C: Type + CpuConfigType> { | ||||||
|     pub unit_num: UnitNum<UnitNumWidth>, |     pub unit_num: UnitNum<C>, | ||||||
|     pub unit_out_reg: UnitOutRegNum<OutRegNumWidth>, |     pub unit_out_reg: UnitOutRegNum<C>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<UnitNumWidth: Size, OutRegNumWidth: Size> PRegNum<UnitNumWidth, OutRegNumWidth> { | impl<C: Type + CpuConfigType> PRegNum<C> { | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     pub fn const_zero(self) -> Expr<Self> { |     pub fn const_zero(self) -> Expr<Self> { | ||||||
|         #[hdl] |         #[hdl] | ||||||
|  | @ -874,6 +882,7 @@ impl<UnitNumWidth: Size, OutRegNumWidth: Size> PRegNum<UnitNumWidth, OutRegNumWi | ||||||
|             unit_out_reg: #[hdl] |             unit_out_reg: #[hdl] | ||||||
|             UnitOutRegNum { |             UnitOutRegNum { | ||||||
|                 value: 0u8.cast_to(self.unit_out_reg.value), |                 value: 0u8.cast_to(self.unit_out_reg.value), | ||||||
|  |                 config: self.unit_out_reg.config, | ||||||
|             }, |             }, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | @ -910,11 +919,9 @@ impl MOpRegNum { | ||||||
|     //
 |     //
 | ||||||
|     // TODO: maybe add more registers later.
 |     // TODO: maybe add more registers later.
 | ||||||
|     pub const FLAG_REG_NUMS: Range<u32> = 0xFE..0x100; |     pub const FLAG_REG_NUMS: Range<u32> = 0xFE..0x100; | ||||||
|     /// registers handled by a special small rename table (for flags and stuff, since it has more read/write ports)
 |     /// registers that aren't constants
 | ||||||
|     pub const SPECIAL_REG_NUMS: Range<u32> = Self::FLAG_REG_NUMS; |     pub const NON_CONST_REG_NUMS: Range<u32> = | ||||||
|     /// registers handled by the large rename table for normal registers (has less read/write ports)
 |         Self::CONST_ZERO_REG_NUM + 1..Self::FLAG_REG_NUMS.end; | ||||||
|     pub const NORMAL_REG_NUMS: Range<u32> = |  | ||||||
|         Self::CONST_ZERO_REG_NUM + 1..Self::SPECIAL_REG_NUMS.start; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[hdl(cmp_eq)] | #[hdl(cmp_eq)] | ||||||
|  | @ -929,29 +936,6 @@ pub struct MOpDestReg { | ||||||
|     pub flag_regs: Array<HdlOption<()>, { range_u32_len(&MOpRegNum::FLAG_REG_NUMS) }>, |     pub flag_regs: Array<HdlOption<()>, { 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<u32> { |  | ||||||
|         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)] | #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] | ||||||
| pub enum MOpDestRegKind { | pub enum MOpDestRegKind { | ||||||
|     NormalReg { |     NormalReg { | ||||||
|  | @ -989,16 +973,13 @@ impl fmt::Display for MOpDestRegName { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl MOpDestRegKind { | impl MOpDestRegKind { | ||||||
|     pub const fn reg_range(self) -> std::ops::Range<u32> { |     pub const fn reg_num_range(self) -> std::ops::Range<u32> { | ||||||
|         match self { |         match self { | ||||||
|             Self::NormalReg { .. } => MOpRegNum::NORMAL_REG_NUMS, |             Self::NormalReg { dest_reg_index: _ } => MOpRegNum::NON_CONST_REG_NUMS, | ||||||
|             Self::FlagReg { .. } => MOpRegNum::FLAG_REG_NUMS, |             Self::FlagReg { | ||||||
|         } |                 reg_num, | ||||||
|     } |                 flag_reg_index: _, | ||||||
|     pub const fn rename_table_names(self) -> &'static [RenameTableName] { |             } => reg_num..reg_num + 1, | ||||||
|         match self { |  | ||||||
|             Self::NormalReg { .. } => &[RenameTableName::Normal, RenameTableName::Special], |  | ||||||
|             Self::FlagReg { .. } => &[RenameTableName::Special], |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     pub fn fixed_reg_num(self) -> Option<u32> { |     pub fn fixed_reg_num(self) -> Option<u32> { | ||||||
|  | @ -1091,5 +1072,5 @@ pub type MOp = UnitMOp< | ||||||
| >; | >; | ||||||
| 
 | 
 | ||||||
| #[hdl] | #[hdl] | ||||||
| pub type RenamedMOp<DestReg: Type, SrcRegWidth: Size> = | pub type RenamedMOp<SrcRegWidth: Size> = | ||||||
|     UnitMOp<DestReg, SrcRegWidth, L2RegisterFileMOp<DestReg, SrcRegWidth>>; |     UnitMOp<(), SrcRegWidth, L2RegisterFileMOp<(), SrcRegWidth>>; | ||||||
|  |  | ||||||
							
								
								
									
										261
									
								
								crates/cpu/src/instruction_rename.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										261
									
								
								crates/cpu/src/instruction_rename.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,261 @@ | ||||||
|  | // 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<C: Type + CpuConfigType> { | ||||||
|  |     pub mop: MOp, | ||||||
|  |     pub pc: UInt<64>, | ||||||
|  |     pub renamed_dest: PRegNum<C>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[hdl(no_static)] | ||||||
|  | struct InsnsInPrefixSummary<C: Type + CpuConfigType> { | ||||||
|  |     all_ready: Bool, | ||||||
|  |     ready_count: Length<FetchWidth<C>>, | ||||||
|  |     retire_queue_used: Length<FetchWidth<C>>, | ||||||
|  |     config: C, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type C = PhantomConst<CpuConfig>; | ||||||
|  | 
 | ||||||
|  | #[hdl] | ||||||
|  | pub type InstructionRenameInsnsOut<C: Type + CpuConfigType> = ArrayType< | ||||||
|  |     ReadyValid<RenamedInsnData<C, RenamedMOp<PRegNumWidth<C>>, PRegNum<C>>>, | ||||||
|  |     FetchWidth<C>, | ||||||
|  | >; | ||||||
|  | 
 | ||||||
|  | #[hdl_module] | ||||||
|  | pub fn instruction_rename(config: PhantomConst<CpuConfig>) { | ||||||
|  |     #[hdl] | ||||||
|  |     let cd: ClockDomain = m.input(); | ||||||
|  |     #[hdl] | ||||||
|  |     let insns_in: ReadyValidArray<InstructionRenameInputInsn<C>, FetchWidth<C>> = | ||||||
|  |         m.input(ReadyValidArray[InstructionRenameInputInsn[config]][FetchWidth[config]]); | ||||||
|  |     #[hdl] | ||||||
|  |     let start_retire_queue_index: RetireQueueIndex<C> = m.input(RetireQueueIndex[config]); | ||||||
|  |     #[hdl] | ||||||
|  |     let end_retire_queue_index: RetireQueueIndex<C> = m.output(RetireQueueIndex[config]); | ||||||
|  |     #[hdl] | ||||||
|  |     let insns_out: InstructionRenameInsnsOut<C> = 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<HdlOption<_>>, move_reg: Expr<MoveRegMOp<_, _>>| { | ||||||
|  |                     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, | ||||||
|  |                     } | ||||||
|  |                 }), | ||||||
|  |             ); | ||||||
|  |         }, | ||||||
|  |     ); | ||||||
|  | } | ||||||
|  | @ -2,7 +2,10 @@ | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
| pub mod config; | pub mod config; | ||||||
| pub mod instruction; | pub mod instruction; | ||||||
|  | pub mod instruction_rename; | ||||||
| pub mod reg_alloc; | pub mod reg_alloc; | ||||||
| pub mod register; | pub mod register; | ||||||
|  | pub mod rename_table; | ||||||
|  | pub mod retire_queue; | ||||||
| pub mod unit; | pub mod unit; | ||||||
| pub mod util; | pub mod util; | ||||||
|  |  | ||||||
|  | @ -3,17 +3,17 @@ | ||||||
| use crate::{ | use crate::{ | ||||||
|     config::CpuConfig, |     config::CpuConfig, | ||||||
|     instruction::{ |     instruction::{ | ||||||
|         MOp, MOpDestReg, MOpRegNum, MOpTrait, MoveRegMOp, PRegNum, RenameTableName, UnitOutRegNum, |         MOp, MOpDestReg, MOpRegNum, MOpTrait, MoveRegMOp, PRegNum, UnitOutRegNum, | ||||||
|         COMMON_MOP_SRC_LEN, |         COMMON_MOP_SRC_LEN, | ||||||
|     }, |     }, | ||||||
|     unit::{ |     unit::{ | ||||||
|         unit_base::{UnitForwardingInfo, UnitInput}, |  | ||||||
|         GlobalState, TrapData, UnitMOp, UnitOutput, UnitOutputWrite, UnitResult, |         GlobalState, TrapData, UnitMOp, UnitOutput, UnitOutputWrite, UnitResult, | ||||||
|         UnitResultCompleted, UnitTrait, |         UnitResultCompleted, UnitTrait, | ||||||
|     }, |     }, | ||||||
|     util::tree_reduce::tree_reduce_with_state, |     util::array_vec::ReadyValidArray, | ||||||
| }; | }; | ||||||
| use fayalite::{ | use fayalite::{ | ||||||
|  |     int::BoolOrIntType, | ||||||
|     memory::{splat_mask, WriteStruct}, |     memory::{splat_mask, WriteStruct}, | ||||||
|     module::{instance_with_loc, memory_with_loc, wire_with_loc}, |     module::{instance_with_loc, memory_with_loc, wire_with_loc}, | ||||||
|     prelude::*, |     prelude::*, | ||||||
|  | @ -44,150 +44,12 @@ pub enum FetchDecodeSpecialOp { | ||||||
| 
 | 
 | ||||||
| #[hdl] | #[hdl] | ||||||
| pub struct FetchDecodeInterface<FetchWidth: Size> { | pub struct FetchDecodeInterface<FetchWidth: Size> { | ||||||
|     pub decoded_insns: ArrayType<ReadyValid<FetchedDecodedMOp>, FetchWidth>, |     pub decoded_insns: ReadyValidArray<FetchedDecodedMOp, FetchWidth>, | ||||||
|     #[hdl(flip)] |     #[hdl(flip)] | ||||||
|     pub fetch_decode_special_op: ReadyValid<FetchDecodeSpecialOp>, |     pub fetch_decode_special_op: ReadyValid<FetchDecodeSpecialOp>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[hdl] | #[cfg(todo)] | ||||||
| struct ROBRenamedInsn<UnitNumWidth: Size, OutRegNumWidth: Size> { |  | ||||||
|     mop_dest: MOpDestReg, |  | ||||||
|     p_dest: PRegNum<UnitNumWidth, OutRegNumWidth>, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[hdl] |  | ||||||
| struct ROBEntry<UnitNumWidth: Size, OutRegNumWidth: Size> { |  | ||||||
|     renamed_insn: ROBRenamedInsn<UnitNumWidth, OutRegNumWidth>, |  | ||||||
|     dest_written: Bool, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[hdl_module] |  | ||||||
| fn rob(config: &CpuConfig) { |  | ||||||
|     #[hdl] |  | ||||||
|     let cd: ClockDomain = m.input(); |  | ||||||
|     #[hdl] |  | ||||||
|     let renamed_insns_in: Array<ReadyValid<ROBRenamedInsn<DynSize, DynSize>>> = m.input( |  | ||||||
|         Array[ReadyValid[ROBRenamedInsn[config.unit_num_width()][config.out_reg_num_width]]] |  | ||||||
|             [config.fetch_width.get()], |  | ||||||
|     ); |  | ||||||
|     #[hdl] |  | ||||||
|     let unit_forwarding_info: UnitForwardingInfo<DynSize, DynSize, DynSize> = |  | ||||||
|         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<UInt>, |  | ||||||
|         range: std::ops::Range<usize>, |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     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::<UInt<1>>(), |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|     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] | #[hdl_module] | ||||||
| /// combination register allocator, register renaming, unit selection, and retire handling
 | /// combination register allocator, register renaming, unit selection, and retire handling
 | ||||||
| pub fn reg_alloc(config: &CpuConfig) { | pub fn reg_alloc(config: &CpuConfig) { | ||||||
|  | @ -205,10 +67,6 @@ pub fn reg_alloc(config: &CpuConfig) { | ||||||
|     ); |     ); | ||||||
|     // TODO: finish
 |     // TODO: finish
 | ||||||
| 
 | 
 | ||||||
|     #[hdl] |  | ||||||
|     let rob = instance(rob(config)); |  | ||||||
|     connect(rob.cd, cd); |  | ||||||
| 
 |  | ||||||
|     let mut rename_table_mems = BTreeMap::<RenameTableName, MemBuilder<_>>::new(); |     let mut rename_table_mems = BTreeMap::<RenameTableName, MemBuilder<_>>::new(); | ||||||
| 
 | 
 | ||||||
|     for reg_kind in MOpDestReg::REG_KINDS { |     for reg_kind in MOpDestReg::REG_KINDS { | ||||||
|  | @ -238,11 +96,6 @@ pub fn reg_alloc(config: &CpuConfig) { | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let renamed_mops_out_reg = wire(Array[HdlOption[config.p_reg_num()]][config.fetch_width.get()]); |     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() { |     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
 |         // TODO: finish
 | ||||||
|         connect( |         connect( | ||||||
|             fetch_decode_interface.decoded_insns[fetch_index].ready, |             fetch_decode_interface.decoded_insns[fetch_index].ready, | ||||||
|  | @ -483,7 +336,6 @@ pub fn reg_alloc(config: &CpuConfig) { | ||||||
|     ); |     ); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let unit_forwarding_info = wire(config.unit_forwarding_info()); |     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() { |     for (unit_index, unit_config) in config.units.iter().enumerate() { | ||||||
|         let dyn_unit = unit_config.kind.unit(config, unit_index); |         let dyn_unit = unit_config.kind.unit(config, unit_index); | ||||||
|         let unit = instance_with_loc( |         let unit = instance_with_loc( | ||||||
|  |  | ||||||
|  | @ -1,7 +1,10 @@ | ||||||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
| use crate::util::tree_reduce::tree_reduce; | use fayalite::{ | ||||||
| use fayalite::{module::wire_with_loc, prelude::*, util::ready_valid::ReadyValid}; |     module::wire_with_loc, | ||||||
|  |     prelude::*, | ||||||
|  |     util::{prefix_sum::reduce, ready_valid::ReadyValid}, | ||||||
|  | }; | ||||||
| use std::{num::NonZeroUsize, ops::Range}; | use std::{num::NonZeroUsize, ops::Range}; | ||||||
| 
 | 
 | ||||||
| #[hdl_module] | #[hdl_module] | ||||||
|  | @ -44,7 +47,7 @@ pub fn unit_free_regs_tracker( | ||||||
|         count, |         count, | ||||||
|         count_overflowed, |         count_overflowed, | ||||||
|         alloc_nums, |         alloc_nums, | ||||||
|     }) = tree_reduce( |     }) = reduce( | ||||||
|         (0..reg_count).map(|index| Summary { |         (0..reg_count).map(|index| Summary { | ||||||
|             range: index..index + 1, |             range: index..index + 1, | ||||||
|             count: (!allocated_reg[index]) |             count: (!allocated_reg[index]) | ||||||
|  |  | ||||||
							
								
								
									
										187
									
								
								crates/cpu/src/rename_table.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								crates/cpu/src/rename_table.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,187 @@ | ||||||
|  | // 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<C: Type + CpuConfigType> { | ||||||
|  |     pub addr: MOpRegNum, | ||||||
|  |     #[hdl(flip)] | ||||||
|  |     pub data: PRegNum<C>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[hdl(no_static)] | ||||||
|  | pub struct RenameTableWritePort<C: Type + CpuConfigType> { | ||||||
|  |     pub addr: MOpRegNum, | ||||||
|  |     pub data: PRegNum<C>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Clone, Debug)] | ||||||
|  | pub enum RenameTablePortConfig { | ||||||
|  |     Read { addr_range: Range<u32> }, | ||||||
|  |     Write { addr_range: Range<u32> }, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type C = PhantomConst<CpuConfig>; | ||||||
|  | 
 | ||||||
|  | /// 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<CpuConfig>, 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<RenameTableReadPort<C>> = | ||||||
|  |         m.input(Array[RenameTableReadPort[config]][read_count]); | ||||||
|  |     #[hdl] | ||||||
|  |     let write_ports: Array<RenameTableWritePort<C>> = | ||||||
|  |         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<MOpRegNum>| { | ||||||
|  |             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); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										16
									
								
								crates/cpu/src/retire_queue.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								crates/cpu/src/retire_queue.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,16 @@ | ||||||
|  | // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||||
|  | // See Notices.txt for copyright information
 | ||||||
|  | 
 | ||||||
|  | use crate::config::CpuConfig; | ||||||
|  | use fayalite::prelude::*; | ||||||
|  | 
 | ||||||
|  | #[hdl] | ||||||
|  | pub struct RenameRetireInterface {} | ||||||
|  | 
 | ||||||
|  | #[hdl_module] | ||||||
|  | pub fn retire_queue(config: &CpuConfig) { | ||||||
|  |     #[hdl] | ||||||
|  |     let cd: ClockDomain = m.input(); | ||||||
|  | 
 | ||||||
|  |     todo!(); | ||||||
|  | } | ||||||
|  | @ -2,19 +2,21 @@ | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
| 
 | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     config::CpuConfig, |     config::{CpuConfig, CpuConfigType, RetireQueueIndexWidth, UnitCount}, | ||||||
|     instruction::{ |     instruction::{ | ||||||
|         mop_enum, AluBranchMOp, LoadStoreMOp, MOp, MOpDestReg, MOpInto, MOpRegNum, MOpTrait, |         mop_enum, AluBranchMOp, LoadStoreMOp, MOp, MOpDestReg, MOpInto, MOpRegNum, MOpTrait, | ||||||
|         RenamedMOp, UnitOutRegNum, |         RenamedMOp, UnitOutRegNum, | ||||||
|     }, |     }, | ||||||
|     register::{FlagsMode, PRegValue}, |     register::{FlagsMode, PRegValue}, | ||||||
|     unit::unit_base::UnitToRegAlloc, |  | ||||||
| }; | }; | ||||||
| use fayalite::{ | use fayalite::{ | ||||||
|     bundle::{Bundle, BundleType}, |     bundle::{Bundle, BundleType}, | ||||||
|  |     int::BoolOrIntType, | ||||||
|     intern::{Intern, Interned}, |     intern::{Intern, Interned}, | ||||||
|     prelude::*, |     prelude::*, | ||||||
|  |     util::ready_valid::ReadyValid, | ||||||
| }; | }; | ||||||
|  | use serde::{Deserialize, Serialize}; | ||||||
| 
 | 
 | ||||||
| pub mod alu_branch; | pub mod alu_branch; | ||||||
| pub mod unit_base; | pub mod unit_base; | ||||||
|  | @ -36,7 +38,7 @@ macro_rules! all_units { | ||||||
|         } |         } | ||||||
|     ) => { |     ) => { | ||||||
|         $(#[$enum_meta])* |         $(#[$enum_meta])* | ||||||
|         #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] |         #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Serialize, Deserialize)] | ||||||
|         $vis enum $UnitKind { |         $vis enum $UnitKind { | ||||||
|             $( |             $( | ||||||
|                 $(#[$variant_meta])* |                 $(#[$variant_meta])* | ||||||
|  | @ -45,7 +47,7 @@ macro_rules! all_units { | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         impl $UnitKind { |         impl $UnitKind { | ||||||
|             pub fn unit(self, config: &CpuConfig, unit_index: usize) -> DynUnit { |             pub fn unit(self, config: PhantomConst<CpuConfig>, unit_index: usize) -> DynUnit { | ||||||
|                 match self { |                 match self { | ||||||
|                     $($UnitKind::$Unit => $create_dyn_unit_fn(config, unit_index),)* |                     $($UnitKind::$Unit => $create_dyn_unit_fn(config, unit_index),)* | ||||||
|                 } |                 } | ||||||
|  | @ -204,24 +206,29 @@ macro_rules! all_units { | ||||||
|             })* |             })* | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         $(impl<$DestReg: Type, $SrcRegWidth: Size> MOpInto<RenamedMOp<$DestReg, $SrcRegWidth>> for $BeforeOp { |         const _: () = { | ||||||
|             fn mop_into_ty(self) -> RenamedMOp<$DestReg, $SrcRegWidth> { |             #[hdl] | ||||||
|                 RenamedMOp[MOpTrait::dest_reg_ty(self)][MOpTrait::src_reg_width(self)] |             type $DestReg = (); | ||||||
|  | 
 | ||||||
|  |             $(impl<$SrcRegWidth: Size> MOpInto<RenamedMOp<$SrcRegWidth>> for $BeforeOp { | ||||||
|  |                 fn mop_into_ty(self) -> RenamedMOp<$SrcRegWidth> { | ||||||
|  |                     RenamedMOp[MOpTrait::src_reg_width(self)] | ||||||
|                 } |                 } | ||||||
|             fn mop_into(this: Expr<Self>) -> Expr<RenamedMOp<$DestReg, $SrcRegWidth>> { |                 fn mop_into(this: Expr<Self>) -> Expr<RenamedMOp<$SrcRegWidth>> { | ||||||
|                 MOpInto::<RenamedMOp<$DestReg, $SrcRegWidth>>::mop_into_ty(Expr::ty(this)).$BeforeUnit(this) |                     MOpInto::<RenamedMOp<$SrcRegWidth>>::mop_into_ty(Expr::ty(this)).$BeforeUnit(this) | ||||||
|                 } |                 } | ||||||
|             })* |             })* | ||||||
| 
 | 
 | ||||||
|         $(impl<$DestReg: Type, $SrcRegWidth: Size> MOpInto<RenamedMOp<$DestReg, $SrcRegWidth>> for $AfterOp { |             $(impl<$SrcRegWidth: Size> MOpInto<RenamedMOp<$SrcRegWidth>> for $AfterOp { | ||||||
|             fn mop_into_ty(self) -> RenamedMOp<$DestReg, $SrcRegWidth> { |                 fn mop_into_ty(self) -> RenamedMOp<$SrcRegWidth> { | ||||||
|                 RenamedMOp[MOpTrait::dest_reg_ty(self)][MOpTrait::src_reg_width(self)] |                     RenamedMOp[MOpTrait::src_reg_width(self)] | ||||||
|                 } |                 } | ||||||
|             fn mop_into(this: Expr<Self>) -> Expr<RenamedMOp<$DestReg, $SrcRegWidth>> { |                 fn mop_into(this: Expr<Self>) -> Expr<RenamedMOp<$SrcRegWidth>> { | ||||||
|                 MOpInto::<RenamedMOp<$DestReg, $SrcRegWidth>>::mop_into_ty(Expr::ty(this)).$AfterUnit(this) |                     MOpInto::<RenamedMOp<$SrcRegWidth>>::mop_into_ty(Expr::ty(this)).$AfterUnit(this) | ||||||
|                 } |                 } | ||||||
|             })* |             })* | ||||||
|         }; |         }; | ||||||
|  |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| all_units! { | all_units! { | ||||||
|  | @ -253,18 +260,79 @@ pub struct GlobalState { | ||||||
|     pub flags_mode: FlagsMode, |     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<C: Type + CpuConfigType> { | ||||||
|  |     /// 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<RetireQueueIndexWidth<C>>, | ||||||
|  |     pub config: C, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<C: Type + CpuConfigType> RetireQueueIndex<C> { | ||||||
|  |     pub fn insns_until( | ||||||
|  |         this: impl ToExpr<Type = Self>, | ||||||
|  |         target: impl ToExpr<Type = Self>, | ||||||
|  |     ) -> Expr<SIntType<RetireQueueIndexWidth<C>>> { | ||||||
|  |         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<C: Type + CpuConfigType, MOp, DestReg> { | ||||||
|  |     pub retire_queue_index: RetireQueueIndex<C>, | ||||||
|  |     pub pc: UInt<64>, | ||||||
|  |     pub dest: DestReg, | ||||||
|  |     pub mop: MOp, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[hdl(no_static)] | ||||||
|  | pub struct UnitForwardingInfo<C: Type + CpuConfigType> { | ||||||
|  |     pub unit_output_writes: ArrayType<HdlOption<UnitOutputWrite<C>>, UnitCount<C>>, | ||||||
|  |     pub unit_reg_frees: ArrayType<HdlOption<UnitOutRegNum<C>>, UnitCount<C>>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[hdl(no_static)] | ||||||
|  | pub struct UnitToRegAlloc<C: Type + CpuConfigType, MOp: Type, ExtraOut: Type> { | ||||||
|  |     #[hdl(flip)] | ||||||
|  |     pub unit_forwarding_info: UnitForwardingInfo<C>, | ||||||
|  |     #[hdl(flip)] | ||||||
|  |     pub input: ReadyValid<RenamedInsnData<C, MOp, UnitOutRegNum<C>>>, | ||||||
|  |     #[hdl(flip)] | ||||||
|  |     pub cancel_input: HdlOption<UnitCancelInput<C>>, | ||||||
|  |     pub output: HdlOption<UnitOutput<C, ExtraOut>>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<C: Type + CpuConfigType, MOp: Type, ExtraOut: Type> UnitToRegAlloc<C, MOp, ExtraOut> { | ||||||
|  |     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)] | #[hdl(cmp_eq)] | ||||||
| pub struct UnitResultCompleted<ExtraOut> { | pub struct UnitResultCompleted<ExtraOut> { | ||||||
|     pub value: PRegValue, |     pub value: PRegValue, | ||||||
|     pub extra_out: ExtraOut, |     pub extra_out: ExtraOut, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[hdl(cmp_eq)] | #[hdl(cmp_eq, no_static)] | ||||||
| pub struct UnitOutputWrite<OutRegNumWidth: Size> { | pub struct UnitOutputWrite<C: Type + CpuConfigType> { | ||||||
|     pub which: UnitOutRegNum<OutRegNumWidth>, |     pub dest: UnitOutRegNum<C>, | ||||||
|     pub value: PRegValue, |     pub value: PRegValue, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[hdl] | ||||||
|  | pub type UnitOutputWrites<C: Type + CpuConfigType> = | ||||||
|  |     ArrayType<HdlOption<UnitOutputWrite<C>>, UnitCount<C>>; | ||||||
|  | 
 | ||||||
| #[hdl(cmp_eq)] | #[hdl(cmp_eq)] | ||||||
| pub struct TrapData { | pub struct TrapData { | ||||||
|     // TODO
 |     // TODO
 | ||||||
|  | @ -282,21 +350,31 @@ impl<ExtraOut: Type> UnitResult<ExtraOut> { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[hdl] | #[hdl(no_static)] | ||||||
| pub struct UnitOutput<OutRegNumWidth: Size, ExtraOut> { | pub struct UnitOutput<C: Type + CpuConfigType, ExtraOut> { | ||||||
|     pub which: UnitOutRegNum<OutRegNumWidth>, |     pub dest: UnitOutRegNum<C>, | ||||||
|  |     pub retire_queue_index: RetireQueueIndex<C>, | ||||||
|     pub result: UnitResult<ExtraOut>, |     pub result: UnitResult<ExtraOut>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<OutRegNumWidth: Size, ExtraOut: Type> UnitOutput<OutRegNumWidth, ExtraOut> { | impl<C: Type + CpuConfigType, ExtraOut: Type> UnitOutput<C, ExtraOut> { | ||||||
|     pub fn extra_out_ty(self) -> ExtraOut { |     pub fn extra_out_ty(self) -> ExtraOut { | ||||||
|         self.result.extra_out_ty() |         self.result.extra_out_ty() | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[hdl(cmp_eq)] | #[hdl(cmp_eq, no_static)] | ||||||
| pub struct UnitCancelInput<OutRegNumWidth: Size> { | pub struct UnitCancelInput<C: Type + CpuConfigType> { | ||||||
|     pub which: UnitOutRegNum<OutRegNumWidth>, |     pub target: RetireQueueIndex<C>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<C: Type + CpuConfigType> UnitCancelInput<C> { | ||||||
|  |     pub fn is_canceled( | ||||||
|  |         this: impl ToExpr<Type = Self>, | ||||||
|  |         insn_retire_queue_index: impl ToExpr<Type = RetireQueueIndex<C>>, | ||||||
|  |     ) -> Expr<Bool> { | ||||||
|  |         RetireQueueIndex::insns_until(insn_retire_queue_index, this.to_expr().target).cmp_ge(0i8) | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub trait UnitTrait: | pub trait UnitTrait: | ||||||
|  | @ -312,17 +390,14 @@ pub trait UnitTrait: | ||||||
| 
 | 
 | ||||||
|     fn unit_kind(&self) -> UnitKind; |     fn unit_kind(&self) -> UnitKind; | ||||||
| 
 | 
 | ||||||
|     fn extract_mop( |     fn extract_mop(&self, mop: Expr<RenamedMOp<DynSize>>) -> Expr<HdlOption<Self::MOp>>; | ||||||
|         &self, |  | ||||||
|         mop: Expr<RenamedMOp<UnitOutRegNum<DynSize>, DynSize>>, |  | ||||||
|     ) -> Expr<HdlOption<Self::MOp>>; |  | ||||||
| 
 | 
 | ||||||
|     fn module(&self) -> Interned<Module<Self::Type>>; |     fn module(&self) -> Interned<Module<Self::Type>>; | ||||||
| 
 | 
 | ||||||
|     fn unit_to_reg_alloc( |     fn unit_to_reg_alloc( | ||||||
|         &self, |         &self, | ||||||
|         this: Expr<Self::Type>, |         this: Expr<Self::Type>, | ||||||
|     ) -> Expr<UnitToRegAlloc<Self::MOp, Self::ExtraOut, DynSize, DynSize, DynSize>>; |     ) -> Expr<UnitToRegAlloc<PhantomConst<CpuConfig>, Self::MOp, Self::ExtraOut>>; | ||||||
| 
 | 
 | ||||||
|     fn cd(&self, this: Expr<Self::Type>) -> Expr<ClockDomain>; |     fn cd(&self, this: Expr<Self::Type>) -> Expr<ClockDomain>; | ||||||
| 
 | 
 | ||||||
|  | @ -370,10 +445,7 @@ impl UnitTrait for DynUnit { | ||||||
|         self.unit_kind |         self.unit_kind | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn extract_mop( |     fn extract_mop(&self, mop: Expr<RenamedMOp<DynSize>>) -> Expr<HdlOption<Self::MOp>> { | ||||||
|         &self, |  | ||||||
|         mop: Expr<RenamedMOp<UnitOutRegNum<DynSize>, DynSize>>, |  | ||||||
|     ) -> Expr<HdlOption<Self::MOp>> { |  | ||||||
|         self.unit.extract_mop(mop) |         self.unit.extract_mop(mop) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -384,7 +456,7 @@ impl UnitTrait for DynUnit { | ||||||
|     fn unit_to_reg_alloc( |     fn unit_to_reg_alloc( | ||||||
|         &self, |         &self, | ||||||
|         this: Expr<Self::Type>, |         this: Expr<Self::Type>, | ||||||
|     ) -> Expr<UnitToRegAlloc<Self::MOp, Self::ExtraOut, DynSize, DynSize, DynSize>> { |     ) -> Expr<UnitToRegAlloc<PhantomConst<CpuConfig>, Self::MOp, Self::ExtraOut>> { | ||||||
|         self.unit.unit_to_reg_alloc(this) |         self.unit.unit_to_reg_alloc(this) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -425,10 +497,7 @@ impl<T: UnitTrait + Clone + std::hash::Hash + Eq> UnitTrait for DynUnitWrapper<T | ||||||
|         self.0.unit_kind() |         self.0.unit_kind() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn extract_mop( |     fn extract_mop(&self, mop: Expr<RenamedMOp<DynSize>>) -> Expr<HdlOption<Self::MOp>> { | ||||||
|         &self, |  | ||||||
|         mop: Expr<RenamedMOp<UnitOutRegNum<DynSize>, DynSize>>, |  | ||||||
|     ) -> Expr<HdlOption<Self::MOp>> { |  | ||||||
|         Expr::from_enum(Expr::as_enum(self.0.extract_mop(mop))) |         Expr::from_enum(Expr::as_enum(self.0.extract_mop(mop))) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -439,7 +508,7 @@ impl<T: UnitTrait + Clone + std::hash::Hash + Eq> UnitTrait for DynUnitWrapper<T | ||||||
|     fn unit_to_reg_alloc( |     fn unit_to_reg_alloc( | ||||||
|         &self, |         &self, | ||||||
|         this: Expr<Self::Type>, |         this: Expr<Self::Type>, | ||||||
|     ) -> Expr<UnitToRegAlloc<Self::MOp, Self::ExtraOut, DynSize, DynSize, DynSize>> { |     ) -> Expr<UnitToRegAlloc<PhantomConst<CpuConfig>, Self::MOp, Self::ExtraOut>> { | ||||||
|         Expr::from_bundle(Expr::as_bundle( |         Expr::from_bundle(Expr::as_bundle( | ||||||
|             self.0.unit_to_reg_alloc(Expr::from_bundle(this)), |             self.0.unit_to_reg_alloc(Expr::from_bundle(this)), | ||||||
|         )) |         )) | ||||||
|  |  | ||||||
|  | @ -2,29 +2,26 @@ | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
| 
 | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     config::CpuConfig, |     config::{CpuConfig, PRegNumWidth}, | ||||||
|     instruction::{ |     instruction::{ | ||||||
|         AddSubMOp, AluBranchMOp, AluCommonMOp, CommonMOp, LogicalMOp, MOpTrait, OutputIntegerMode, |         AddSubMOp, AluBranchMOp, AluCommonMOp, CommonMOp, LogicalMOp, OutputIntegerMode, | ||||||
|         RenamedMOp, UnitOutRegNum, COMMON_MOP_SRC_LEN, |         RenamedMOp, COMMON_MOP_SRC_LEN, | ||||||
|     }, |     }, | ||||||
|     register::{FlagsMode, PRegFlagsPowerISA, PRegFlagsX86, PRegValue}, |     register::{FlagsMode, PRegFlagsPowerISA, PRegFlagsX86, PRegValue}, | ||||||
|     unit::{ |     unit::{ | ||||||
|         unit_base::{unit_base, ExecuteEnd, ExecuteStart, UnitToRegAlloc}, |         unit_base::{unit_base, ExecuteEnd, ExecuteStart}, | ||||||
|         DynUnit, DynUnitWrapper, GlobalState, UnitKind, UnitMOp, UnitOutput, UnitResult, |         DynUnit, DynUnitWrapper, GlobalState, UnitKind, UnitMOp, UnitOutput, UnitResult, | ||||||
|         UnitResultCompleted, UnitTrait, |         UnitResultCompleted, UnitToRegAlloc, UnitTrait, | ||||||
|     }, |     }, | ||||||
| }; | }; | ||||||
| use fayalite::{ | use fayalite::{ | ||||||
|     intern::{Intern, Interned}, |     intern::Interned, module::wire_with_loc, prelude::*, util::ready_valid::ReadyValid, | ||||||
|     module::wire_with_loc, |  | ||||||
|     prelude::*, |  | ||||||
|     util::ready_valid::ReadyValid, |  | ||||||
| }; | }; | ||||||
| use std::{collections::HashMap, ops::RangeTo}; | use std::{collections::HashMap, ops::RangeTo}; | ||||||
| 
 | 
 | ||||||
| #[hdl] | #[hdl] | ||||||
| fn add_sub<SrcCount: KnownSize>( | fn add_sub<SrcCount: KnownSize>( | ||||||
|     mop: Expr<AddSubMOp<UnitOutRegNum<DynSize>, DynSize, SrcCount>>, |     mop: Expr<AddSubMOp<(), DynSize, SrcCount>>, | ||||||
|     pc: Expr<UInt<64>>, |     pc: Expr<UInt<64>>, | ||||||
|     flags_mode: Expr<FlagsMode>, |     flags_mode: Expr<FlagsMode>, | ||||||
|     src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>, |     src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>, | ||||||
|  | @ -232,7 +229,7 @@ fn add_sub<SrcCount: KnownSize>( | ||||||
| 
 | 
 | ||||||
| #[hdl] | #[hdl] | ||||||
| fn logical( | fn logical( | ||||||
|     mop: Expr<LogicalMOp<UnitOutRegNum<DynSize>, DynSize>>, |     mop: Expr<LogicalMOp<(), DynSize>>, | ||||||
|     flags_mode: Expr<FlagsMode>, |     flags_mode: Expr<FlagsMode>, | ||||||
|     src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>, |     src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>, | ||||||
| ) -> Expr<UnitResultCompleted<()>> { | ) -> Expr<UnitResultCompleted<()>> { | ||||||
|  | @ -245,20 +242,15 @@ fn logical( | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[hdl_module] | #[hdl_module] | ||||||
| pub fn alu_branch(config: &CpuConfig, unit_index: usize) { | pub fn alu_branch(config: PhantomConst<CpuConfig>, unit_index: usize) { | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let cd: ClockDomain = m.input(); |     let cd: ClockDomain = m.input(); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let unit_to_reg_alloc: UnitToRegAlloc< |     let unit_to_reg_alloc: UnitToRegAlloc< | ||||||
|         AluBranchMOp<UnitOutRegNum<DynSize>, DynSize>, |         PhantomConst<CpuConfig>, | ||||||
|  |         AluBranchMOp<(), DynSize>, | ||||||
|         (), |         (), | ||||||
|         DynSize, |     > = m.output(UnitToRegAlloc[config][AluBranchMOp[()][PRegNumWidth[config]]][()]); | ||||||
|         DynSize, |  | ||||||
|         DynSize, |  | ||||||
|     > = m.output(config.unit_to_reg_alloc( |  | ||||||
|         AluBranchMOp[config.unit_out_reg_num()][config.p_reg_num_width()], |  | ||||||
|         (), |  | ||||||
|     )); |  | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let global_state: GlobalState = m.input(); |     let global_state: GlobalState = m.input(); | ||||||
| 
 | 
 | ||||||
|  | @ -279,13 +271,9 @@ pub fn alu_branch(config: &CpuConfig, unit_index: usize) { | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     if let HdlSome(execute_start) = ReadyValid::firing_data(unit_base.execute_start) { |     if let HdlSome(execute_start) = ReadyValid::firing_data(unit_base.execute_start) { | ||||||
|         #[hdl] |         #[hdl] | ||||||
|         let ExecuteStart::<_> { |         let ExecuteStart::<_, _> { insn, src_values } = execute_start; | ||||||
|             mop, |  | ||||||
|             pc, |  | ||||||
|             src_values, |  | ||||||
|         } = execute_start; |  | ||||||
|         #[hdl] |         #[hdl] | ||||||
|         match mop { |         match insn.mop { | ||||||
|             AluBranchMOp::<_, _>::AddSub(mop) => connect( |             AluBranchMOp::<_, _>::AddSub(mop) => connect( | ||||||
|                 unit_base.execute_end, |                 unit_base.execute_end, | ||||||
|                 HdlSome( |                 HdlSome( | ||||||
|  | @ -293,10 +281,11 @@ pub fn alu_branch(config: &CpuConfig, unit_index: usize) { | ||||||
|                     ExecuteEnd::<_, _> { |                     ExecuteEnd::<_, _> { | ||||||
|                         unit_output: #[hdl] |                         unit_output: #[hdl] | ||||||
|                         UnitOutput::<_, _> { |                         UnitOutput::<_, _> { | ||||||
|                             which: MOpTrait::dest_reg(mop), |                             dest: insn.dest, | ||||||
|  |                             retire_queue_index: insn.retire_queue_index, | ||||||
|                             result: UnitResult[()].Completed(add_sub( |                             result: UnitResult[()].Completed(add_sub( | ||||||
|                                 mop, |                                 mop, | ||||||
|                                 pc, |                                 insn.pc, | ||||||
|                                 global_state.flags_mode, |                                 global_state.flags_mode, | ||||||
|                                 src_values, |                                 src_values, | ||||||
|                             )), |                             )), | ||||||
|  | @ -311,10 +300,11 @@ pub fn alu_branch(config: &CpuConfig, unit_index: usize) { | ||||||
|                     ExecuteEnd::<_, _> { |                     ExecuteEnd::<_, _> { | ||||||
|                         unit_output: #[hdl] |                         unit_output: #[hdl] | ||||||
|                         UnitOutput::<_, _> { |                         UnitOutput::<_, _> { | ||||||
|                             which: MOpTrait::dest_reg(mop), |                             dest: insn.dest, | ||||||
|  |                             retire_queue_index: insn.retire_queue_index, | ||||||
|                             result: UnitResult[()].Completed(add_sub( |                             result: UnitResult[()].Completed(add_sub( | ||||||
|                                 mop, |                                 mop, | ||||||
|                                 pc, |                                 insn.pc, | ||||||
|                                 global_state.flags_mode, |                                 global_state.flags_mode, | ||||||
|                                 src_values, |                                 src_values, | ||||||
|                             )), |                             )), | ||||||
|  | @ -329,7 +319,8 @@ pub fn alu_branch(config: &CpuConfig, unit_index: usize) { | ||||||
|                     ExecuteEnd::<_, _> { |                     ExecuteEnd::<_, _> { | ||||||
|                         unit_output: #[hdl] |                         unit_output: #[hdl] | ||||||
|                         UnitOutput::<_, _> { |                         UnitOutput::<_, _> { | ||||||
|                             which: MOpTrait::dest_reg(mop), |                             dest: insn.dest, | ||||||
|  |                             retire_queue_index: insn.retire_queue_index, | ||||||
|                             result: UnitResult[()].Completed(logical( |                             result: UnitResult[()].Completed(logical( | ||||||
|                                 mop, |                                 mop, | ||||||
|                                 global_state.flags_mode, |                                 global_state.flags_mode, | ||||||
|  | @ -345,14 +336,14 @@ pub fn alu_branch(config: &CpuConfig, unit_index: usize) { | ||||||
| 
 | 
 | ||||||
| #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | ||||||
| pub struct AluBranch { | pub struct AluBranch { | ||||||
|     config: Interned<CpuConfig>, |     config: PhantomConst<CpuConfig>, | ||||||
|     module: Interned<Module<alu_branch>>, |     module: Interned<Module<alu_branch>>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl AluBranch { | impl AluBranch { | ||||||
|     pub fn new(config: &CpuConfig, unit_index: usize) -> Self { |     pub fn new(config: PhantomConst<CpuConfig>, unit_index: usize) -> Self { | ||||||
|         Self { |         Self { | ||||||
|             config: config.intern(), |             config, | ||||||
|             module: alu_branch(config, unit_index), |             module: alu_branch(config, unit_index), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | @ -361,7 +352,7 @@ impl AluBranch { | ||||||
| impl UnitTrait for AluBranch { | impl UnitTrait for AluBranch { | ||||||
|     type Type = alu_branch; |     type Type = alu_branch; | ||||||
|     type ExtraOut = (); |     type ExtraOut = (); | ||||||
|     type MOp = AluBranchMOp<UnitOutRegNum<DynSize>, DynSize>; |     type MOp = AluBranchMOp<(), DynSize>; | ||||||
| 
 | 
 | ||||||
|     fn ty(&self) -> Self::Type { |     fn ty(&self) -> Self::Type { | ||||||
|         self.module.io_ty() |         self.module.io_ty() | ||||||
|  | @ -379,10 +370,7 @@ impl UnitTrait for AluBranch { | ||||||
|         UnitKind::AluBranch |         UnitKind::AluBranch | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn extract_mop( |     fn extract_mop(&self, mop: Expr<RenamedMOp<DynSize>>) -> Expr<HdlOption<Self::MOp>> { | ||||||
|         &self, |  | ||||||
|         mop: Expr<RenamedMOp<UnitOutRegNum<DynSize>, DynSize>>, |  | ||||||
|     ) -> Expr<HdlOption<Self::MOp>> { |  | ||||||
|         UnitMOp::alu_branch_mop(mop) |         UnitMOp::alu_branch_mop(mop) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -393,7 +381,7 @@ impl UnitTrait for AluBranch { | ||||||
|     fn unit_to_reg_alloc( |     fn unit_to_reg_alloc( | ||||||
|         &self, |         &self, | ||||||
|         this: Expr<Self::Type>, |         this: Expr<Self::Type>, | ||||||
|     ) -> Expr<UnitToRegAlloc<Self::MOp, Self::ExtraOut, DynSize, DynSize, DynSize>> { |     ) -> Expr<UnitToRegAlloc<PhantomConst<CpuConfig>, Self::MOp, Self::ExtraOut>> { | ||||||
|         this.unit_to_reg_alloc |         this.unit_to_reg_alloc | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -2,72 +2,31 @@ | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
| 
 | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     config::CpuConfig, |     config::{CpuConfig, CpuConfigType, UnitOutRegNumWidth}, | ||||||
|     instruction::{MOpTrait, PRegNum, UnitNum, UnitOutRegNum, COMMON_MOP_SRC_LEN}, |     instruction::{MOpTrait, PRegNum, UnitNum, UnitOutRegNum, COMMON_MOP_SRC_LEN}, | ||||||
|     register::PRegValue, |     register::PRegValue, | ||||||
|     unit::{UnitCancelInput, UnitOutput, UnitOutputWrite}, |     unit::{ | ||||||
|     util::tree_reduce::tree_reduce, |         RenamedInsnData, UnitCancelInput, UnitForwardingInfo, UnitOutput, UnitOutputWrite, | ||||||
|  |         UnitToRegAlloc, | ||||||
|  |     }, | ||||||
| }; | }; | ||||||
| use fayalite::{ | use fayalite::{ | ||||||
|     memory::splat_mask, |     memory::splat_mask, | ||||||
|     module::{memory_with_loc, wire_with_loc}, |     module::{memory_with_loc, wire_with_loc}, | ||||||
|     prelude::*, |     prelude::*, | ||||||
|     ty::StaticType, |     ty::StaticType, | ||||||
|     util::ready_valid::ReadyValid, |     util::{prefix_sum::reduce, ready_valid::ReadyValid}, | ||||||
| }; | }; | ||||||
| use std::marker::PhantomData; |  | ||||||
| 
 | 
 | ||||||
| #[hdl] | #[hdl(no_static)] | ||||||
| pub struct UnitForwardingInfo<UnitNumWidth: Size, OutRegNumWidth: Size, UnitCount: Size> { | pub struct ExecuteStart<C: Type + CpuConfigType, MOp: Type> { | ||||||
|     pub unit_output_writes: ArrayType<HdlOption<UnitOutputWrite<OutRegNumWidth>>, UnitCount>, |     pub insn: RenamedInsnData<C, MOp, UnitOutRegNum<C>>, | ||||||
|     pub unit_reg_frees: ArrayType<HdlOption<UnitOutRegNum<OutRegNumWidth>>, UnitCount>, |  | ||||||
|     pub _phantom: PhantomData<UnitNumWidth>, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[hdl] |  | ||||||
| pub struct UnitInput<MOp: Type> { |  | ||||||
|     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<UnitNumWidth, OutRegNumWidth, UnitCount>, |  | ||||||
|     #[hdl(flip)] |  | ||||||
|     pub input: ReadyValid<UnitInput<MOp>>, |  | ||||||
|     #[hdl(flip)] |  | ||||||
|     pub cancel_input: HdlOption<UnitCancelInput<OutRegNumWidth>>, |  | ||||||
|     pub output: HdlOption<UnitOutput<OutRegNumWidth, ExtraOut>>, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<MOp: Type, ExtraOut: Type, UnitNumWidth: Size, OutRegNumWidth: Size, UnitCount: Size> |  | ||||||
|     UnitToRegAlloc<MOp, ExtraOut, UnitNumWidth, OutRegNumWidth, UnitCount> |  | ||||||
| { |  | ||||||
|     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<MOp: Type + MOpTrait<DestReg = UnitOutRegNum<DynSize>>> { |  | ||||||
|     pub mop: MOp, |  | ||||||
|     pub pc: UInt<64>, |  | ||||||
|     pub src_values: Array<PRegValue, { COMMON_MOP_SRC_LEN }>, |     pub src_values: Array<PRegValue, { COMMON_MOP_SRC_LEN }>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[hdl] | #[hdl(no_static)] | ||||||
| pub struct ExecuteEnd<OutRegNumWidth: Size, ExtraOut> { | pub struct ExecuteEnd<C: Type + CpuConfigType, ExtraOut> { | ||||||
|     pub unit_output: UnitOutput<OutRegNumWidth, ExtraOut>, |     pub unit_output: UnitOutput<C, ExtraOut>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[hdl] | #[hdl] | ||||||
|  | @ -147,11 +106,10 @@ impl InFlightOpState { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[hdl] | #[hdl(no_static)] | ||||||
| struct InFlightOp<MOp: Type> { | struct InFlightOp<C: Type + CpuConfigType, MOp: Type> { | ||||||
|     state: InFlightOpState, |     state: InFlightOpState, | ||||||
|     mop: MOp, |     insn: RenamedInsnData<C, MOp, UnitOutRegNum<C>>, | ||||||
|     pc: UInt<64>, |  | ||||||
|     src_ready_flags: Array<Bool, { COMMON_MOP_SRC_LEN }>, |     src_ready_flags: Array<Bool, { COMMON_MOP_SRC_LEN }>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -166,7 +124,7 @@ impl<OpIndexWidth: Size> InFlightOpsSummary<OpIndexWidth> { | ||||||
|     fn new<MOp: Type>( |     fn new<MOp: Type>( | ||||||
|         op_index: usize, |         op_index: usize, | ||||||
|         op_index_ty: UIntType<OpIndexWidth>, |         op_index_ty: UIntType<OpIndexWidth>, | ||||||
|         in_flight_op: impl ToExpr<Type = HdlOption<InFlightOp<MOp>>>, |         in_flight_op: impl ToExpr<Type = HdlOption<InFlightOp<PhantomConst<CpuConfig>, MOp>>>, | ||||||
|     ) -> Expr<Self> { |     ) -> Expr<Self> { | ||||||
|         let empty_op_index = wire_with_loc( |         let empty_op_index = wire_with_loc( | ||||||
|             &format!("empty_op_index_{op_index}"), |             &format!("empty_op_index_{op_index}"), | ||||||
|  | @ -183,10 +141,9 @@ impl<OpIndexWidth: Size> InFlightOpsSummary<OpIndexWidth> { | ||||||
|         #[hdl] |         #[hdl] | ||||||
|         if let HdlSome(in_flight_op) = in_flight_op { |         if let HdlSome(in_flight_op) = in_flight_op { | ||||||
|             #[hdl] |             #[hdl] | ||||||
|             let InFlightOp::<_> { |             let InFlightOp::<_, _> { | ||||||
|                 state, |                 state, | ||||||
|                 mop: _, |                 insn: _, | ||||||
|                 pc: _, |  | ||||||
|                 src_ready_flags, |                 src_ready_flags, | ||||||
|             } = in_flight_op; |             } = in_flight_op; | ||||||
|             connect(ready_op_index, HdlOption[op_index_ty].HdlNone()); |             connect(ready_op_index, HdlOption[op_index_ty].HdlNone()); | ||||||
|  | @ -224,13 +181,15 @@ impl<OpIndexWidth: Size> InFlightOpsSummary<OpIndexWidth> { | ||||||
| 
 | 
 | ||||||
| impl InFlightOpsSummary<DynSize> { | impl InFlightOpsSummary<DynSize> { | ||||||
|     fn summarize<MOp: Type, MaxInFlight: Size>( |     fn summarize<MOp: Type, MaxInFlight: Size>( | ||||||
|         in_flight_ops: impl ToExpr<Type = ArrayType<HdlOption<InFlightOp<MOp>>, MaxInFlight>>, |         in_flight_ops: impl ToExpr< | ||||||
|  |             Type = ArrayType<HdlOption<InFlightOp<PhantomConst<CpuConfig>, MOp>>, MaxInFlight>, | ||||||
|  |         >, | ||||||
|     ) -> Expr<Self> { |     ) -> Expr<Self> { | ||||||
|         let in_flight_ops = in_flight_ops.to_expr(); |         let in_flight_ops = in_flight_ops.to_expr(); | ||||||
|         let max_in_flight = Expr::ty(in_flight_ops).len(); |         let max_in_flight = Expr::ty(in_flight_ops).len(); | ||||||
|         let index_range = 0..max_in_flight; |         let index_range = 0..max_in_flight; | ||||||
|         let index_ty = UInt::range(index_range.clone()); |         let index_ty = UInt::range(index_range.clone()); | ||||||
|         tree_reduce( |         reduce( | ||||||
|             index_range.map(|i| Self::new(i, index_ty, in_flight_ops[i])), |             index_range.map(|i| Self::new(i, index_ty, in_flight_ops[i])), | ||||||
|             Self::combine, |             Self::combine, | ||||||
|         ) |         ) | ||||||
|  | @ -239,11 +198,8 @@ impl InFlightOpsSummary<DynSize> { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[hdl_module] | #[hdl_module] | ||||||
| pub fn unit_base< | pub fn unit_base<MOp: Type + MOpTrait<DestReg = (), SrcRegWidth = DynSize>, ExtraOut: Type>( | ||||||
|     MOp: Type + MOpTrait<DestReg = UnitOutRegNum<DynSize>, SrcRegWidth = DynSize>, |     config: PhantomConst<CpuConfig>, | ||||||
|     ExtraOut: Type, |  | ||||||
| >( |  | ||||||
|     config: &CpuConfig, |  | ||||||
|     unit_index: usize, |     unit_index: usize, | ||||||
|     mop_ty: MOp, |     mop_ty: MOp, | ||||||
|     extra_out_ty: ExtraOut, |     extra_out_ty: ExtraOut, | ||||||
|  | @ -251,18 +207,19 @@ pub fn unit_base< | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let cd: ClockDomain = m.input(); |     let cd: ClockDomain = m.input(); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let unit_to_reg_alloc: UnitToRegAlloc<MOp, ExtraOut, DynSize, DynSize, DynSize> = |     let unit_to_reg_alloc: UnitToRegAlloc<PhantomConst<CpuConfig>, MOp, ExtraOut> = | ||||||
|         m.output(config.unit_to_reg_alloc(mop_ty, extra_out_ty)); |         m.output(UnitToRegAlloc[config][mop_ty][extra_out_ty]); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let execute_start: ReadyValid<ExecuteStart<MOp>> = m.output(ReadyValid[ExecuteStart[mop_ty]]); |     let execute_start: ReadyValid<ExecuteStart<PhantomConst<CpuConfig>, MOp>> = | ||||||
|  |         m.output(ReadyValid[ExecuteStart[config][mop_ty]]); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let execute_end: HdlOption<ExecuteEnd<DynSize, ExtraOut>> = |     let execute_end: HdlOption<ExecuteEnd<PhantomConst<CpuConfig>, ExtraOut>> = | ||||||
|         m.input(HdlOption[ExecuteEnd[config.out_reg_num_width][extra_out_ty]]); |         m.input(HdlOption[ExecuteEnd[config][extra_out_ty]]); | ||||||
| 
 | 
 | ||||||
|     connect(execute_start.data, Expr::ty(execute_start).data.HdlNone()); |     connect(execute_start.data, Expr::ty(execute_start).data.HdlNone()); | ||||||
| 
 | 
 | ||||||
|     let max_in_flight = config.unit_max_in_flight(unit_index).get(); |     let max_in_flight = config.get().unit_max_in_flight(unit_index).get(); | ||||||
|     let in_flight_op_ty = InFlightOp[mop_ty]; |     let in_flight_op_ty = InFlightOp[config][mop_ty]; | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let in_flight_ops = reg_builder() |     let in_flight_ops = reg_builder() | ||||||
|         .clock_domain(cd) |         .clock_domain(cd) | ||||||
|  | @ -279,16 +236,15 @@ pub fn unit_base< | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let UnitForwardingInfo::<_, _, _> { |     let UnitForwardingInfo::<_> { | ||||||
|         unit_output_writes, |         unit_output_writes, | ||||||
|         unit_reg_frees, |         unit_reg_frees, | ||||||
|         _phantom: _, |  | ||||||
|     } = unit_to_reg_alloc.unit_forwarding_info; |     } = unit_to_reg_alloc.unit_forwarding_info; | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let read_src_regs = wire(mop_ty.src_regs_ty()); |     let read_src_regs = wire(mop_ty.src_regs_ty()); | ||||||
|     connect( |     connect( | ||||||
|         read_src_regs, |         read_src_regs, | ||||||
|         repeat(config.p_reg_num().const_zero().cast_to_bits(), ConstUsize), |         repeat(PRegNum[config].const_zero().cast_to_bits(), ConstUsize), | ||||||
|     ); |     ); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let read_src_values = wire(); |     let read_src_values = wire(); | ||||||
|  | @ -297,7 +253,7 @@ pub fn unit_base< | ||||||
|     let input_src_regs = wire(mop_ty.src_regs_ty()); |     let input_src_regs = wire(mop_ty.src_regs_ty()); | ||||||
|     connect( |     connect( | ||||||
|         input_src_regs, |         input_src_regs, | ||||||
|         repeat(config.p_reg_num().const_zero().cast_to_bits(), ConstUsize), |         repeat(PRegNum[config].const_zero().cast_to_bits(), ConstUsize), | ||||||
|     ); |     ); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let input_src_regs_valid = wire(); |     let input_src_regs_valid = wire(); | ||||||
|  | @ -309,7 +265,7 @@ pub fn unit_base< | ||||||
|                 Bool, |                 Bool, | ||||||
|                 SourceLocation::caller(), |                 SourceLocation::caller(), | ||||||
|             ); |             ); | ||||||
|             mem.depth(1 << config.out_reg_num_width); |             mem.depth(1 << UnitOutRegNumWidth[config]); | ||||||
|             mem |             mem | ||||||
|         }) |         }) | ||||||
|         .collect(); |         .collect(); | ||||||
|  | @ -319,11 +275,11 @@ pub fn unit_base< | ||||||
|             PRegValue, |             PRegValue, | ||||||
|             SourceLocation::caller(), |             SourceLocation::caller(), | ||||||
|         ); |         ); | ||||||
|         unit_output_regs.depth(1 << config.out_reg_num_width); |         unit_output_regs.depth(1 << UnitOutRegNumWidth[config]); | ||||||
| 
 | 
 | ||||||
|         for src_index in 0..COMMON_MOP_SRC_LEN { |         for src_index in 0..COMMON_MOP_SRC_LEN { | ||||||
|             let read_port = unit_output_regs.new_read_port(); |             let read_port = unit_output_regs.new_read_port(); | ||||||
|             let p_reg_num = read_src_regs[src_index].cast_bits_to(config.p_reg_num()); |             let p_reg_num = read_src_regs[src_index].cast_bits_to(PRegNum[config]); | ||||||
|             connect_any(read_port.addr, p_reg_num.unit_out_reg.value); |             connect_any(read_port.addr, p_reg_num.unit_out_reg.value); | ||||||
|             connect(read_port.en, false); |             connect(read_port.en, false); | ||||||
|             connect(read_port.clk, cd.clk); |             connect(read_port.clk, cd.clk); | ||||||
|  | @ -336,7 +292,7 @@ pub fn unit_base< | ||||||
| 
 | 
 | ||||||
|         for src_index in 0..COMMON_MOP_SRC_LEN { |         for src_index in 0..COMMON_MOP_SRC_LEN { | ||||||
|             let read_port = unit_output_regs_valid[unit_index].new_read_port(); |             let read_port = unit_output_regs_valid[unit_index].new_read_port(); | ||||||
|             let p_reg_num = input_src_regs[src_index].cast_bits_to(config.p_reg_num()); |             let p_reg_num = input_src_regs[src_index].cast_bits_to(PRegNum[config]); | ||||||
|             connect_any(read_port.addr, p_reg_num.unit_out_reg.value); |             connect_any(read_port.addr, p_reg_num.unit_out_reg.value); | ||||||
|             connect(read_port.en, false); |             connect(read_port.en, false); | ||||||
|             connect(read_port.clk, cd.clk); |             connect(read_port.clk, cd.clk); | ||||||
|  | @ -361,15 +317,15 @@ pub fn unit_base< | ||||||
|         connect(ready_write_port.mask, true); |         connect(ready_write_port.mask, true); | ||||||
|         #[hdl] |         #[hdl] | ||||||
|         if let HdlSome(unit_output_write) = unit_output_writes[unit_index] { |         if let HdlSome(unit_output_write) = unit_output_writes[unit_index] { | ||||||
|             connect_any(write_port.addr, unit_output_write.which.value); |             connect_any(write_port.addr, unit_output_write.dest.value); | ||||||
|             connect(write_port.data, unit_output_write.value); |             connect(write_port.data, unit_output_write.value); | ||||||
|             connect(write_port.en, true); |             connect(write_port.en, true); | ||||||
|             connect_any(ready_write_port.addr, unit_output_write.which.value); |             connect_any(ready_write_port.addr, unit_output_write.dest.value); | ||||||
|             connect(ready_write_port.en, true); |             connect(ready_write_port.en, true); | ||||||
|             let p_reg_num = #[hdl] |             let p_reg_num = #[hdl] | ||||||
|             PRegNum::<_, _> { |             PRegNum::<_> { | ||||||
|                 unit_num: config.unit_num().from_index(unit_index), |                 unit_num: UnitNum[config].from_index(unit_index), | ||||||
|                 unit_out_reg: unit_output_write.which, |                 unit_out_reg: unit_output_write.dest, | ||||||
|             }; |             }; | ||||||
|             for src_index in 0..COMMON_MOP_SRC_LEN { |             for src_index in 0..COMMON_MOP_SRC_LEN { | ||||||
|                 #[hdl] |                 #[hdl] | ||||||
|  | @ -399,9 +355,8 @@ pub fn unit_base< | ||||||
|                 execute_start.data, |                 execute_start.data, | ||||||
|                 HdlSome( |                 HdlSome( | ||||||
|                     #[hdl] |                     #[hdl] | ||||||
|                     ExecuteStart::<_> { |                     ExecuteStart::<_, _> { | ||||||
|                         mop: in_flight_op.mop, |                         insn: in_flight_op.insn, | ||||||
|                         pc: in_flight_op.pc, |  | ||||||
|                         src_values: read_src_values, |                         src_values: read_src_values, | ||||||
|                     }, |                     }, | ||||||
|                 ), |                 ), | ||||||
|  | @ -420,12 +375,17 @@ pub fn unit_base< | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     if let HdlSome(input) = ReadyValid::firing_data(unit_to_reg_alloc.input) { |     if let HdlSome(input) = ReadyValid::firing_data(unit_to_reg_alloc.input) { | ||||||
|         #[hdl] |         #[hdl] | ||||||
|         let UnitInput::<_> { mop, pc } = input; |         let RenamedInsnData::<_, _, _> { | ||||||
|  |             retire_queue_index, | ||||||
|  |             pc: _, | ||||||
|  |             dest: _, | ||||||
|  |             mop, | ||||||
|  |         } = input; | ||||||
|         #[hdl] |         #[hdl] | ||||||
|         let input_mop_src_regs = wire(mop_ty.src_regs_ty()); |         let input_mop_src_regs = wire(mop_ty.src_regs_ty()); | ||||||
|         connect( |         connect( | ||||||
|             input_mop_src_regs, |             input_mop_src_regs, | ||||||
|             repeat(config.p_reg_num().const_zero().cast_to_bits(), ConstUsize), |             repeat(PRegNum[config].const_zero().cast_to_bits(), ConstUsize), | ||||||
|         ); |         ); | ||||||
|         MOp::connect_src_regs(mop, input_mop_src_regs); |         MOp::connect_src_regs(mop, input_mop_src_regs); | ||||||
|         let src_ready_flags = wire_with_loc( |         let src_ready_flags = wire_with_loc( | ||||||
|  | @ -436,20 +396,24 @@ pub fn unit_base< | ||||||
|         connect(src_ready_flags, input_src_regs_valid); |         connect(src_ready_flags, input_src_regs_valid); | ||||||
|         connect(input_src_regs, input_mop_src_regs); |         connect(input_src_regs, input_mop_src_regs); | ||||||
|         #[hdl] |         #[hdl] | ||||||
|         if unit_to_reg_alloc.cancel_input.cmp_ne(HdlSome( |         let input_is_canceled = wire(); | ||||||
|  |         connect(input_is_canceled, false); | ||||||
|         #[hdl] |         #[hdl] | ||||||
|             UnitCancelInput::<_> { |         if let HdlSome(cancel_input) = unit_to_reg_alloc.cancel_input { | ||||||
|                 which: MOp::dest_reg(mop), |             connect( | ||||||
|             }, |                 input_is_canceled, | ||||||
|         )) { |                 UnitCancelInput::is_canceled(cancel_input, retire_queue_index), | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|  |         #[hdl] | ||||||
|  |         if !input_is_canceled { | ||||||
|             connect( |             connect( | ||||||
|                 input_in_flight_op, |                 input_in_flight_op, | ||||||
|                 HdlSome( |                 HdlSome( | ||||||
|                     #[hdl] |                     #[hdl] | ||||||
|                     InFlightOp::<_> { |                     InFlightOp::<_, _> { | ||||||
|                         state: InFlightOpState.Ready(), |                         state: InFlightOpState.Ready(), | ||||||
|                         mop, |                         insn: input, | ||||||
|                         pc, |  | ||||||
|                         src_ready_flags, |                         src_ready_flags, | ||||||
|                     }, |                     }, | ||||||
|                 ), |                 ), | ||||||
|  | @ -483,13 +447,11 @@ pub fn unit_base< | ||||||
|         #[hdl] |         #[hdl] | ||||||
|         if let HdlSome(in_flight_op) = in_flight_ops[in_flight_op_index] { |         if let HdlSome(in_flight_op) = in_flight_ops[in_flight_op_index] { | ||||||
|             #[hdl] |             #[hdl] | ||||||
|             let InFlightOp::<_> { |             let InFlightOp::<_, _> { | ||||||
|                 state, |                 state, | ||||||
|                 mop, |                 insn, | ||||||
|                 pc, |  | ||||||
|                 src_ready_flags, |                 src_ready_flags, | ||||||
|             } = in_flight_op; |             } = in_flight_op; | ||||||
|             let which = MOp::dest_reg(mop); |  | ||||||
|             let src_regs = wire_with_loc( |             let src_regs = wire_with_loc( | ||||||
|                 &format!("in_flight_op_src_regs_{in_flight_op_index}"), |                 &format!("in_flight_op_src_regs_{in_flight_op_index}"), | ||||||
|                 SourceLocation::caller(), |                 SourceLocation::caller(), | ||||||
|  | @ -497,9 +459,9 @@ pub fn unit_base< | ||||||
|             ); |             ); | ||||||
|             connect( |             connect( | ||||||
|                 src_regs, |                 src_regs, | ||||||
|                 repeat(config.p_reg_num().const_zero().cast_to_bits(), ConstUsize), |                 repeat(PRegNum[config].const_zero().cast_to_bits(), ConstUsize), | ||||||
|             ); |             ); | ||||||
|             MOp::connect_src_regs(mop, src_regs); |             MOp::connect_src_regs(insn.mop, src_regs); | ||||||
| 
 | 
 | ||||||
|             #[hdl] |             #[hdl] | ||||||
|             if in_flight_ops_summary.ready_op_index.cmp_eq(HdlSome( |             if in_flight_ops_summary.ready_op_index.cmp_eq(HdlSome( | ||||||
|  | @ -517,12 +479,12 @@ pub fn unit_base< | ||||||
|                 if let HdlSome(unit_output_write) = unit_output_writes[unit_index] { |                 if let HdlSome(unit_output_write) = unit_output_writes[unit_index] { | ||||||
|                     #[hdl] |                     #[hdl] | ||||||
|                     let UnitOutputWrite::<_> { |                     let UnitOutputWrite::<_> { | ||||||
|                         which: unit_out_reg, |                         dest: unit_out_reg, | ||||||
|                         value: _, |                         value: _, | ||||||
|                     } = unit_output_write; |                     } = unit_output_write; | ||||||
|                     let p_reg_num = #[hdl] |                     let p_reg_num = #[hdl] | ||||||
|                     PRegNum::<_, _> { |                     PRegNum::<_> { | ||||||
|                         unit_num: config.unit_num().from_index(unit_index), |                         unit_num: UnitNum[config].from_index(unit_index), | ||||||
|                         unit_out_reg, |                         unit_out_reg, | ||||||
|                     }; |                     }; | ||||||
|                     for src_index in 0..COMMON_MOP_SRC_LEN { |                     for src_index in 0..COMMON_MOP_SRC_LEN { | ||||||
|  | @ -537,20 +499,21 @@ pub fn unit_base< | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  |             connect(in_flight_op_canceling[in_flight_op_index], false); | ||||||
|  |             #[hdl] | ||||||
|  |             if let HdlSome(cancel_input) = unit_to_reg_alloc.cancel_input { | ||||||
|                 connect( |                 connect( | ||||||
|                     in_flight_op_canceling[in_flight_op_index], |                     in_flight_op_canceling[in_flight_op_index], | ||||||
|                 unit_to_reg_alloc.cancel_input.cmp_eq(HdlSome( |                     UnitCancelInput::is_canceled(cancel_input, insn.retire_queue_index), | ||||||
|                     #[hdl] |  | ||||||
|                     UnitCancelInput::<_> { which }, |  | ||||||
|                 )), |  | ||||||
|                 ); |                 ); | ||||||
|  |             } | ||||||
| 
 | 
 | ||||||
|             #[hdl] |             #[hdl] | ||||||
|             if let HdlSome(execute_end) = execute_end { |             if let HdlSome(execute_end) = execute_end { | ||||||
|                 #[hdl] |                 #[hdl] | ||||||
|                 let ExecuteEnd::<_, _> { unit_output } = execute_end; |                 let ExecuteEnd::<_, _> { unit_output } = execute_end; | ||||||
|                 #[hdl] |                 #[hdl] | ||||||
|                 if which.cmp_eq(unit_output.which) { |                 if insn.dest.cmp_eq(unit_output.dest) { | ||||||
|                     connect(in_flight_op_execute_ending[in_flight_op_index], true); |                     connect(in_flight_op_execute_ending[in_flight_op_index], true); | ||||||
|                     #[hdl] |                     #[hdl] | ||||||
|                     if !in_flight_op_canceling[in_flight_op_index] { |                     if !in_flight_op_canceling[in_flight_op_index] { | ||||||
|  | @ -567,7 +530,7 @@ pub fn unit_base< | ||||||
|             #[hdl] |             #[hdl] | ||||||
|             if let HdlSome(execute_start) = ReadyValid::firing_data(execute_start) { |             if let HdlSome(execute_start) = ReadyValid::firing_data(execute_start) { | ||||||
|                 #[hdl] |                 #[hdl] | ||||||
|                 if which.cmp_eq(MOp::dest_reg(execute_start.mop)) { |                 if insn.dest.cmp_eq(execute_start.insn.dest) { | ||||||
|                     connect(in_flight_op_execute_starting[in_flight_op_index], true); |                     connect(in_flight_op_execute_starting[in_flight_op_index], true); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  | @ -594,10 +557,9 @@ pub fn unit_base< | ||||||
|                     in_flight_ops[in_flight_op_index], |                     in_flight_ops[in_flight_op_index], | ||||||
|                     HdlSome( |                     HdlSome( | ||||||
|                         #[hdl] |                         #[hdl] | ||||||
|                         InFlightOp::<_> { |                         InFlightOp::<_, _> { | ||||||
|                             state, |                             state, | ||||||
|                             mop, |                             insn, | ||||||
|                             pc, |  | ||||||
|                             src_ready_flags: in_flight_op_next_src_ready_flags[in_flight_op_index], |                             src_ready_flags: in_flight_op_next_src_ready_flags[in_flight_op_index], | ||||||
|                         }, |                         }, | ||||||
|                     ), |                     ), | ||||||
|  |  | ||||||
|  | @ -2,7 +2,6 @@ | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
| 
 | 
 | ||||||
| pub mod array_vec; | pub mod array_vec; | ||||||
| pub mod tree_reduce; |  | ||||||
| 
 | 
 | ||||||
| pub(crate) const fn range_u32_len(range: &std::ops::Range<u32>) -> usize { | pub(crate) const fn range_u32_len(range: &std::ops::Range<u32>) -> usize { | ||||||
|     let retval = range.end.saturating_sub(range.start); |     let retval = range.end.saturating_sub(range.start); | ||||||
|  | @ -25,3 +24,16 @@ pub(crate) const fn range_u32_nth_or_panic(range: &std::ops::Range<u32>, index: | ||||||
|         panic!("index out of range") |         panic!("index out of range") | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | pub(crate) const fn range_intersection( | ||||||
|  |     a: &std::ops::Range<u32>, | ||||||
|  |     b: &std::ops::Range<u32>, | ||||||
|  | ) -> Option<std::ops::Range<u32>> { | ||||||
|  |     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 | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -2,8 +2,11 @@ | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
| 
 | 
 | ||||||
| use fayalite::{ | use fayalite::{ | ||||||
|     expr::ops::{ExprCastTo, ExprIndex, ExprPartialEq, ExprPartialOrd}, |     expr::{ | ||||||
|     int::SizeType, |         ops::{ExprCastTo, ExprIndex, ExprPartialEq, ExprPartialOrd}, | ||||||
|  |         ToLiteralBits, | ||||||
|  |     }, | ||||||
|  |     int::{IntType, SizeType}, | ||||||
|     intern::{Intern, Interned}, |     intern::{Intern, Interned}, | ||||||
|     prelude::*, |     prelude::*, | ||||||
|     ty::{MatchVariantWithoutScope, StaticType, TypeProperties}, |     ty::{MatchVariantWithoutScope, StaticType, TypeProperties}, | ||||||
|  | @ -249,6 +252,29 @@ impl<T: Type, N: Size> ArrayVec<T, N> { | ||||||
|         }); |         }); | ||||||
|         array_vec_as_array_of_options |         array_vec_as_array_of_options | ||||||
|     } |     } | ||||||
|  |     #[hdl] | ||||||
|  |     pub fn get<Idx: IntType<Dyn = UInt>>( | ||||||
|  |         this: impl ToExpr<Type = Self>, | ||||||
|  |         index: impl ToExpr<Type = Idx>, | ||||||
|  |     ) -> Expr<HdlOption<T>> { | ||||||
|  |         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<T: Type, N: Size, Idx, IdxWidth: Size> ExprIndex<Idx> for ArrayVec<T, N> | impl<T: Type, N: Size, Idx, IdxWidth: Size> ExprIndex<Idx> for ArrayVec<T, N> | ||||||
|  | @ -263,3 +289,35 @@ where | ||||||
|         <ArrayType<T, N> as ExprIndex<Idx>>::expr_index(&this.elements, index) |         <ArrayType<T, N> as ExprIndex<Idx>>::expr_index(&this.elements, index) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | #[hdl] | ||||||
|  | pub struct ReadyValidArray<T: Type, N: Size> { | ||||||
|  |     pub data: ArrayVec<T, N>, | ||||||
|  |     #[hdl(flip)] | ||||||
|  |     pub ready: Length<N>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T: Type, N: Size> ReadyValidArray<T, N> { | ||||||
|  |     #[hdl] | ||||||
|  |     pub fn firing_len(this: impl ToExpr<Type = Self>) -> Expr<Length<N>> { | ||||||
|  |         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<Type = Self>) -> Expr<ArrayVec<T, N>> { | ||||||
|  |         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 | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -1,152 +0,0 @@ | ||||||
| // 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<Entry>, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 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<Self::Item> { |  | ||||||
|         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<S, I, R>( |  | ||||||
|     iter: impl IntoIterator<IntoIter: ExactSizeIterator, Item = I>, |  | ||||||
|     state: &mut S, |  | ||||||
|     mut input: impl FnMut(&mut S, I) -> R, |  | ||||||
|     mut reduce: impl FnMut(&mut S, R, R) -> R, |  | ||||||
| ) -> Option<R> { |  | ||||||
|     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<T>( |  | ||||||
|     iter: impl IntoIterator<Item = T, IntoIter: ExactSizeIterator>, |  | ||||||
|     mut reduce: impl FnMut(T, T) -> T, |  | ||||||
| ) -> Option<T> { |  | ||||||
|     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<usize>, ops: &mut Vec<TreeReduceOp>) { |  | ||||||
|         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::<Vec<_>>(), |  | ||||||
|                 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() |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue