forked from libre-chip/cpu
		
	WIP implementing unit_base
This commit is contained in:
		
							parent
							
								
									ece788dda3
								
							
						
					
					
						commit
						3f6e5cc600
					
				
					 10 changed files with 17918 additions and 8115 deletions
				
			
		|  | @ -1,8 +1,11 @@ | |||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||
| // See Notices.txt for copyright information
 | ||||
| use crate::{ | ||||
|     instruction::{PRegNum, UnitNum, UnitOutRegNum, CONST_ZERO_UNIT_NUM}, | ||||
|     unit::{unit_base::UnitForwardingInfo, UnitCancelInput, UnitKind, UnitMOp, UnitOutputWrite}, | ||||
|     instruction::{MOpTrait, PRegNum, UnitNum, UnitOutRegNum, CONST_ZERO_UNIT_NUM}, | ||||
|     unit::{ | ||||
|         unit_base::{UnitForwardingInfo, UnitToRegAlloc}, | ||||
|         UnitCancelInput, UnitKind, UnitMOp, UnitOutputWrite, | ||||
|     }, | ||||
| }; | ||||
| use fayalite::prelude::*; | ||||
| use std::num::NonZeroUsize; | ||||
|  | @ -95,4 +98,20 @@ impl CpuConfig { | |||
|             .max_in_flight | ||||
|             .unwrap_or(self.default_unit_max_in_flight) | ||||
|     } | ||||
|     pub fn unit_to_reg_alloc< | ||||
|         MOp: Type + MOpTrait<DestReg = UnitOutRegNum<DynSize>, SrcRegWidth = DynSize>, | ||||
|         ExtraOut: Type, | ||||
|     >( | ||||
|         &self, | ||||
|         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()] | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -1,7 +1,11 @@ | |||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||
| // See Notices.txt for copyright information
 | ||||
| use crate::{unit::UnitMOp, util::range_u32_len}; | ||||
| use fayalite::{expr::ops::ArrayLiteral, intern::Interned, prelude::*}; | ||||
| use fayalite::{ | ||||
|     expr::ops::{ArrayLiteral, ExprPartialEq}, | ||||
|     intern::Interned, | ||||
|     prelude::*, | ||||
| }; | ||||
| use std::{fmt, marker::PhantomData, ops::Range}; | ||||
| 
 | ||||
| pub mod power_isa; | ||||
|  | @ -12,10 +16,26 @@ pub trait MOpTrait: Type { | |||
|     type SrcRegWidth: Size; | ||||
|     fn dest_reg_ty(self) -> Self::DestReg; | ||||
|     fn dest_reg(input: impl ToExpr<Type = Self>) -> Expr<Self::DestReg>; | ||||
|     fn src_reg_width(self) -> <Self::SrcRegWidth as Size>::SizeType; | ||||
|     fn src_reg_ty(self) -> UIntType<Self::SrcRegWidth> { | ||||
|         UInt[self.src_reg_width()] | ||||
|     } | ||||
|     fn src_regs_ty(self) -> Array<UIntType<Self::SrcRegWidth>, { COMMON_MOP_SRC_LEN }> { | ||||
|         Array[self.src_reg_ty()][ConstUsize::<{ COMMON_MOP_SRC_LEN }>] | ||||
|     } | ||||
|     fn for_each_src_reg( | ||||
|         input: impl ToExpr<Type = Self>, | ||||
|         f: &mut impl FnMut(Expr<UIntType<Self::SrcRegWidth>>, usize), | ||||
|     ); | ||||
|     fn connect_src_regs( | ||||
|         input: impl ToExpr<Type = Self>, | ||||
|         src_regs: impl ToExpr<Type = Array<UIntType<Self::SrcRegWidth>, { COMMON_MOP_SRC_LEN }>>, | ||||
|     ) { | ||||
|         let src_regs = src_regs.to_expr(); | ||||
|         Self::for_each_src_reg(input.to_expr(), &mut |src_reg, index| { | ||||
|             connect(src_regs[index], src_reg); | ||||
|         }); | ||||
|     } | ||||
|     fn mapped_ty<NewDestReg: Type, NewSrcRegWidth: Size>( | ||||
|         self, | ||||
|         new_dest_reg: NewDestReg, | ||||
|  | @ -67,6 +87,9 @@ impl<T: CommonMOpTrait> MOpTrait for T { | |||
|     fn dest_reg(input: impl ToExpr<Type = Self>) -> Expr<Self::DestReg> { | ||||
|         T::common_mop(input).dest | ||||
|     } | ||||
|     fn src_reg_width(self) -> <Self::SrcRegWidth as Size>::SizeType { | ||||
|         self.common_mop_ty().src.element().width | ||||
|     } | ||||
|     fn for_each_src_reg( | ||||
|         input: impl ToExpr<Type = Self>, | ||||
|         f: &mut impl FnMut(Expr<UIntType<Self::SrcRegWidth>>, usize), | ||||
|  | @ -129,13 +152,23 @@ pub enum OutputIntegerMode { | |||
|     SignExt8, | ||||
| } | ||||
| 
 | ||||
| impl ExprPartialEq<Self> for OutputIntegerMode { | ||||
|     fn cmp_eq(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> { | ||||
|         lhs.cast_to_bits().cmp_eq(rhs.cast_to_bits()) | ||||
|     } | ||||
| 
 | ||||
|     fn cmp_ne(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> { | ||||
|         lhs.cast_to_bits().cmp_ne(rhs.cast_to_bits()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub const MOP_IMM_WIDTH: usize = 34; | ||||
| pub const MOP_MIN_REG_WIDTH: usize = 8; | ||||
| pub const COMMON_MOP_SRC_LEN: usize = 3; | ||||
| pub const COMMON_MOP_MIN_SRC_LEN_WITH_FULL_IMM: usize = 2; | ||||
| pub const COMMON_MOP_IMM_LOW_WIDTH: usize = CommonMOpWithMaxSrcCount::IMM_WIDTH - 1; | ||||
| 
 | ||||
| #[hdl] | ||||
| #[hdl(cmp_eq)] | ||||
| pub struct CommonMOp<PrefixPad: KnownSize, DestReg: Type, SrcRegWidth: Size, SrcCount: KnownSize> { | ||||
|     pub prefix_pad: UIntType<PrefixPad>, | ||||
|     pub dest: DestReg, | ||||
|  | @ -181,7 +214,7 @@ impl<PrefixPad: KnownSize, DestReg: Type, SrcRegWidth: Size, SrcCount: KnownSize | |||
|     } | ||||
| } | ||||
| 
 | ||||
| #[hdl] | ||||
| #[hdl(cmp_eq)] | ||||
| pub struct CommonMOpImmParts<ImmInSrcCount: Size> { | ||||
|     // fields must be in this exact order
 | ||||
|     pub imm_low: UInt<{ COMMON_MOP_IMM_LOW_WIDTH }>, | ||||
|  | @ -410,6 +443,9 @@ macro_rules! mop_enum { | |||
|                 } | ||||
|                 dest_reg | ||||
|             } | ||||
|             fn src_reg_width(self) -> <Self::SrcRegWidth as Size>::SizeType { | ||||
|                 self.$FirstVariant.src_reg_width() | ||||
|             } | ||||
|             #[hdl] | ||||
|             fn for_each_src_reg( | ||||
|                 input: impl ToExpr<Type = Self>, | ||||
|  | @ -458,7 +494,7 @@ pub(crate) use mop_enum; | |||
| 
 | ||||
| common_mop_struct! { | ||||
|     #[mapped(<NewDestReg, NewSrcRegWidth> AluCommonMOp<NewDestReg, NewSrcRegWidth, SrcCount>)] | ||||
|     #[hdl] | ||||
|     #[hdl(cmp_eq)] | ||||
|     pub struct AluCommonMOp<DestReg: Type, SrcRegWidth: Size, SrcCount: KnownSize> { | ||||
|         #[common] | ||||
|         pub common: CommonMOp<ConstUsize<0>, DestReg, SrcRegWidth, SrcCount>, | ||||
|  | @ -468,7 +504,7 @@ common_mop_struct! { | |||
| 
 | ||||
| common_mop_struct! { | ||||
|     #[mapped(<NewDestReg, NewSrcRegWidth> AddSubMOp<NewDestReg, NewSrcRegWidth, SrcCount>)] | ||||
|     #[hdl] | ||||
|     #[hdl(cmp_eq)] | ||||
|     pub struct AddSubMOp<DestReg: Type, SrcRegWidth: Size, SrcCount: KnownSize> { | ||||
|         #[common] | ||||
|         pub alu_common: AluCommonMOp<DestReg, SrcRegWidth, SrcCount>, | ||||
|  | @ -481,7 +517,7 @@ common_mop_struct! { | |||
| 
 | ||||
| common_mop_struct! { | ||||
|     #[mapped(<NewDestReg, NewSrcRegWidth> LogicalMOp<NewDestReg, NewSrcRegWidth>)] | ||||
|     #[hdl] | ||||
|     #[hdl(cmp_eq)] | ||||
|     pub struct LogicalMOp<DestReg: Type, SrcRegWidth: Size> { | ||||
|         #[common] | ||||
|         pub alu_common: AluCommonMOp<DestReg, SrcRegWidth, ConstUsize<2>>, | ||||
|  | @ -491,7 +527,7 @@ common_mop_struct! { | |||
| 
 | ||||
| common_mop_struct! { | ||||
|     #[mapped(<NewDestReg, NewSrcRegWidth> BranchMOp<NewDestReg, NewSrcRegWidth>)] | ||||
|     #[hdl] | ||||
|     #[hdl(cmp_eq)] | ||||
|     pub struct BranchMOp<DestReg: Type, SrcRegWidth: Size> { | ||||
|         #[common] | ||||
|         pub alu_common: AluCommonMOp<DestReg, SrcRegWidth, ConstUsize<2>>, | ||||
|  | @ -510,7 +546,7 @@ mop_enum! { | |||
| 
 | ||||
| common_mop_struct! { | ||||
|     #[mapped(<NewDestReg, NewSrcRegWidth> ReadL2RegMOp<NewDestReg, NewSrcRegWidth>)] | ||||
|     #[hdl] | ||||
|     #[hdl(cmp_eq)] | ||||
|     pub struct ReadL2RegMOp<DestReg: Type, SrcRegWidth: Size> { | ||||
|         #[common] | ||||
|         pub common: CommonMOp<ConstUsize<1>, DestReg, SrcRegWidth, ConstUsize<0>>, | ||||
|  | @ -519,7 +555,7 @@ common_mop_struct! { | |||
| 
 | ||||
| common_mop_struct! { | ||||
|     #[mapped(<NewDestReg, NewSrcRegWidth> WriteL2RegMOp<NewDestReg, NewSrcRegWidth>)] | ||||
|     #[hdl] | ||||
|     #[hdl(cmp_eq)] | ||||
|     pub struct WriteL2RegMOp<DestReg: Type, SrcRegWidth: Size> { | ||||
|         #[common] | ||||
|         pub common: CommonMOp<ConstUsize<1>, DestReg, SrcRegWidth, ConstUsize<1>>, | ||||
|  | @ -536,7 +572,7 @@ mop_enum! { | |||
| 
 | ||||
| common_mop_struct! { | ||||
|     #[mapped(<NewDestReg, NewSrcRegWidth> LoadStoreCommonMOp<NewDestReg, NewSrcRegWidth, SrcCount>)] | ||||
|     #[hdl] | ||||
|     #[hdl(cmp_eq)] | ||||
|     pub struct LoadStoreCommonMOp<DestReg: Type, SrcRegWidth: Size, SrcCount: KnownSize> { | ||||
|         #[common] | ||||
|         pub common: CommonMOp<ConstUsize<1>, DestReg, SrcRegWidth, SrcCount>, | ||||
|  | @ -545,7 +581,7 @@ common_mop_struct! { | |||
| 
 | ||||
| common_mop_struct! { | ||||
|     #[mapped(<NewDestReg, NewSrcRegWidth> LoadMOp<NewDestReg, NewSrcRegWidth>)] | ||||
|     #[hdl] | ||||
|     #[hdl(cmp_eq)] | ||||
|     pub struct LoadMOp<DestReg: Type, SrcRegWidth: Size> { | ||||
|         #[common] | ||||
|         pub load_store_common: LoadStoreCommonMOp<DestReg, SrcRegWidth, ConstUsize<1>>, | ||||
|  | @ -554,7 +590,7 @@ common_mop_struct! { | |||
| 
 | ||||
| common_mop_struct! { | ||||
|     #[mapped(<NewDestReg, NewSrcRegWidth> StoreMOp<NewDestReg, NewSrcRegWidth>)] | ||||
|     #[hdl] | ||||
|     #[hdl(cmp_eq)] | ||||
|     pub struct StoreMOp<DestReg: Type, SrcRegWidth: Size> { | ||||
|         #[common] | ||||
|         pub load_store_common: LoadStoreCommonMOp<DestReg, SrcRegWidth, ConstUsize<2>>, | ||||
|  | @ -569,7 +605,7 @@ mop_enum! { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| #[hdl] | ||||
| #[hdl(cmp_eq)] | ||||
| /// there may be more than one unit of a given kind, so UnitNum is not the same as UnitKind.
 | ||||
| /// zero is used for built-in constants, such as the zero register
 | ||||
| pub struct UnitNum<Width: Size> { | ||||
|  | @ -617,12 +653,12 @@ impl<Width: Size> UnitNum<Width> { | |||
| 
 | ||||
| pub const CONST_ZERO_UNIT_NUM: usize = 0; | ||||
| 
 | ||||
| #[hdl] | ||||
| #[hdl(cmp_eq)] | ||||
| pub struct UnitOutRegNum<Width: Size> { | ||||
|     pub value: UIntType<Width>, | ||||
| } | ||||
| 
 | ||||
| #[hdl] | ||||
| #[hdl(cmp_eq)] | ||||
| /// Physical Register Number -- registers in the CPU's backend
 | ||||
| pub struct PRegNum<UnitNumWidth: Size, OutRegNumWidth: Size> { | ||||
|     pub unit_num: UnitNum<UnitNumWidth>, | ||||
|  | @ -643,7 +679,7 @@ impl<UnitNumWidth: Size, OutRegNumWidth: Size> PRegNum<UnitNumWidth, OutRegNumWi | |||
|     } | ||||
| } | ||||
| 
 | ||||
| #[hdl] | ||||
| #[hdl(cmp_eq)] | ||||
| /// µOp Register Number -- register in a micro-operation before register renaming
 | ||||
| #[doc(alias = "UOpRegNum")] // help you find it in the docs if you mis-spell it
 | ||||
| #[doc(alias = "\u{B5}OpRegNum")] // micro sign
 | ||||
|  | @ -681,7 +717,7 @@ impl MOpRegNum { | |||
|         Self::CONST_ZERO_REG_NUM + 1..Self::SPECIAL_REG_NUMS.start; | ||||
| } | ||||
| 
 | ||||
| #[hdl] | ||||
| #[hdl(cmp_eq)] | ||||
| /// all the registers this instruction will write to, they are all renamed to the same physical register.
 | ||||
| pub struct MOpDestReg { | ||||
|     /// some instructions have multiple destination registers, e.g. x86 div
 | ||||
|  |  | |||
|  | @ -4,22 +4,22 @@ use crate::{instruction::MOpRegNum, util::range_u32_nth_or_panic}; | |||
| use fayalite::prelude::*; | ||||
| use std::ops::Range; | ||||
| 
 | ||||
| #[hdl] | ||||
| #[hdl(cmp_eq)] | ||||
| pub struct PowerIsaRegNum { | ||||
|     pub value: UInt<5>, | ||||
| } | ||||
| 
 | ||||
| #[hdl] | ||||
| #[hdl(cmp_eq)] | ||||
| pub struct PowerIsaFRegNum { | ||||
|     pub value: UInt<5>, | ||||
| } | ||||
| 
 | ||||
| #[hdl] | ||||
| #[hdl(cmp_eq)] | ||||
| pub struct PowerIsaCrFieldNum { | ||||
|     pub value: UInt<3>, | ||||
| } | ||||
| 
 | ||||
| #[hdl] | ||||
| #[hdl(cmp_eq)] | ||||
| pub struct PowerIsaCrBitNum { | ||||
|     pub cr_field: PowerIsaCrFieldNum, | ||||
|     pub bit_in_field: UInt<2>, | ||||
|  |  | |||
|  | @ -311,7 +311,7 @@ pub fn reg_alloc(config: &CpuConfig) { | |||
|             SourceLocation::caller(), | ||||
|         ); | ||||
|         connect(dyn_unit.cd(unit), cd); | ||||
|         let unit_input_insn = dyn_unit.input_insn(unit); | ||||
|         let unit_to_reg_alloc = dyn_unit.unit_to_reg_alloc(unit); | ||||
|         // TODO: handle assigning multiple instructions to a unit at a time
 | ||||
|         let assign_to_unit_at_once = NonZeroUsize::new(1).unwrap(); | ||||
|         // TODO: handle retiring multiple instructions from a unit at a time
 | ||||
|  | @ -333,8 +333,8 @@ pub fn reg_alloc(config: &CpuConfig) { | |||
|         ); | ||||
|         connect(unit_free_regs_tracker.alloc_out[0].ready, false); | ||||
|         connect( | ||||
|             unit_input_insn.data, | ||||
|             Expr::ty(unit_input_insn).data.HdlNone(), | ||||
|             unit_to_reg_alloc.input_insn.data, | ||||
|             Expr::ty(unit_to_reg_alloc.input_insn).data.HdlNone(), | ||||
|         ); | ||||
|         for fetch_index in 0..config.fetch_width.get() { | ||||
|             #[hdl] | ||||
|  | @ -343,7 +343,7 @@ pub fn reg_alloc(config: &CpuConfig) { | |||
|                 connect(available_units[fetch_index][unit_index], false); | ||||
|             } | ||||
|             #[hdl] | ||||
|             if !unit_input_insn.ready { | ||||
|             if !unit_to_reg_alloc.input_insn.ready { | ||||
|                 // must come after to override connects in loops above
 | ||||
|                 connect(available_units[fetch_index][unit_index], false); | ||||
|             } | ||||
|  | @ -356,11 +356,11 @@ pub fn reg_alloc(config: &CpuConfig) { | |||
|                     if let HdlSome(renamed_mop) = | ||||
|                         HdlOption::and_then(renamed_mops[fetch_index], |v| dyn_unit.extract_mop(v)) | ||||
|                     { | ||||
|                         connect(unit_input_insn.data, HdlSome(renamed_mop)); | ||||
|                         connect(unit_to_reg_alloc.input_insn.data, HdlSome(renamed_mop)); | ||||
|                     } else { | ||||
|                         connect( | ||||
|                             unit_input_insn.data, | ||||
|                             HdlSome(Expr::ty(unit_input_insn).data.HdlSome.uninit()), | ||||
|                             unit_to_reg_alloc.input_insn.data, | ||||
|                             HdlSome(Expr::ty(unit_to_reg_alloc.input_insn).data.HdlSome.uninit()), | ||||
|                         ); | ||||
|                         // FIXME: add hdl_assert(cd.clk, false.to_expr(), "");
 | ||||
|                     } | ||||
|  | @ -387,7 +387,7 @@ pub fn reg_alloc(config: &CpuConfig) { | |||
|         } | ||||
|         // TODO: connect outputs to other units
 | ||||
|         connect( | ||||
|             dyn_unit.unit_forwarding_info(unit), | ||||
|             unit_to_reg_alloc.unit_forwarding_info, | ||||
|             #[hdl] | ||||
|             UnitForwardingInfo::<_, _, _> { | ||||
|                 unit_output_writes: repeat( | ||||
|  | @ -397,10 +397,9 @@ pub fn reg_alloc(config: &CpuConfig) { | |||
|                 _phantom: PhantomData, | ||||
|             }, | ||||
|         ); | ||||
|         connect(dyn_unit.output(unit).ready, false); | ||||
|         // TODO: handle cancellation
 | ||||
|         connect( | ||||
|             dyn_unit.cancel_input(unit).data, | ||||
|             unit_to_reg_alloc.cancel_input, | ||||
|             HdlOption[config.unit_cancel_input()].HdlNone(), | ||||
|         ); | ||||
|     } | ||||
|  |  | |||
|  | @ -8,7 +8,7 @@ pub enum FlagsMode { | |||
|     X86(PRegFlagsX86), | ||||
| } | ||||
| 
 | ||||
| #[hdl] | ||||
| #[hdl(cmp_eq)] | ||||
| pub struct PRegFlagsPowerISA {} | ||||
| 
 | ||||
| impl PRegFlagsPowerISA { | ||||
|  | @ -56,7 +56,7 @@ impl PRegFlagsPowerISA { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| #[hdl] | ||||
| #[hdl(cmp_eq)] | ||||
| pub struct PRegFlagsX86 {} | ||||
| 
 | ||||
| impl PRegFlagsX86 { | ||||
|  | @ -100,7 +100,7 @@ impl PRegFlagsX86 { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| #[hdl] | ||||
| #[hdl(cmp_eq)] | ||||
| /// this is *not* the same as any particular ISA's flags register,
 | ||||
| /// on PowerISA it is a combination of some bits from XER with a single 4-bit CR field.
 | ||||
| ///
 | ||||
|  | @ -138,7 +138,7 @@ impl PRegFlags { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| #[hdl] | ||||
| #[hdl(cmp_eq)] | ||||
| /// Unit output register's value -- a combination of an integer/fp register
 | ||||
| /// and flags register and CR field.
 | ||||
| ///
 | ||||
|  |  | |||
|  | @ -7,13 +7,12 @@ use crate::{ | |||
|         mop_enum, AluBranchMOp, L2RegisterFileMOp, LoadStoreMOp, MOpTrait, UnitOutRegNum, | ||||
|     }, | ||||
|     register::PRegValue, | ||||
|     unit::unit_base::UnitForwardingInfo, | ||||
|     unit::unit_base::UnitToRegAlloc, | ||||
| }; | ||||
| use fayalite::{ | ||||
|     bundle::{Bundle, BundleType}, | ||||
|     intern::{Intern, Interned}, | ||||
|     prelude::*, | ||||
|     util::ready_valid::ReadyValid, | ||||
| }; | ||||
| 
 | ||||
| pub mod alu_branch; | ||||
|  | @ -143,19 +142,19 @@ all_units! { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| #[hdl] | ||||
| #[hdl(cmp_eq)] | ||||
| pub struct UnitResultCompleted<ExtraOut> { | ||||
|     pub value: PRegValue, | ||||
|     pub extra_out: ExtraOut, | ||||
| } | ||||
| 
 | ||||
| #[hdl] | ||||
| #[hdl(cmp_eq)] | ||||
| pub struct UnitOutputWrite<OutRegNumWidth: Size> { | ||||
|     pub which: UnitOutRegNum<OutRegNumWidth>, | ||||
|     pub value: PRegValue, | ||||
| } | ||||
| 
 | ||||
| #[hdl] | ||||
| #[hdl(cmp_eq)] | ||||
| pub struct TrapData { | ||||
|     // TODO
 | ||||
| } | ||||
|  | @ -166,13 +165,25 @@ pub enum UnitResult<ExtraOut> { | |||
|     Trap(TrapData), | ||||
| } | ||||
| 
 | ||||
| impl<ExtraOut: Type> UnitResult<ExtraOut> { | ||||
|     pub fn extra_out_ty(self) -> ExtraOut { | ||||
|         self.Completed.extra_out | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[hdl] | ||||
| pub struct UnitOutput<OutRegNumWidth: Size, ExtraOut> { | ||||
|     pub which: UnitOutRegNum<OutRegNumWidth>, | ||||
|     pub result: UnitResult<ExtraOut>, | ||||
| } | ||||
| 
 | ||||
| #[hdl] | ||||
| impl<OutRegNumWidth: Size, ExtraOut: Type> UnitOutput<OutRegNumWidth, ExtraOut> { | ||||
|     pub fn extra_out_ty(self) -> ExtraOut { | ||||
|         self.result.extra_out_ty() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[hdl(cmp_eq)] | ||||
| pub struct UnitCancelInput<OutRegNumWidth: Size> { | ||||
|     pub which: UnitOutRegNum<OutRegNumWidth>, | ||||
| } | ||||
|  | @ -197,19 +208,10 @@ pub trait UnitTrait: | |||
| 
 | ||||
|     fn module(&self) -> Interned<Module<Self::Type>>; | ||||
| 
 | ||||
|     fn input_insn(&self, this: Expr<Self::Type>) -> Expr<ReadyValid<Self::MOp>>; | ||||
| 
 | ||||
|     fn cancel_input(&self, this: Expr<Self::Type>) -> Expr<ReadyValid<UnitCancelInput<DynSize>>>; | ||||
| 
 | ||||
|     fn unit_forwarding_info( | ||||
|     fn unit_to_reg_alloc( | ||||
|         &self, | ||||
|         this: Expr<Self::Type>, | ||||
|     ) -> Expr<UnitForwardingInfo<DynSize, DynSize, DynSize>>; | ||||
| 
 | ||||
|     fn output( | ||||
|         &self, | ||||
|         this: Expr<Self::Type>, | ||||
|     ) -> Expr<ReadyValid<UnitOutput<DynSize, Self::ExtraOut>>>; | ||||
|     ) -> Expr<UnitToRegAlloc<Self::MOp, Self::ExtraOut, DynSize, DynSize, DynSize>>; | ||||
| 
 | ||||
|     fn cd(&self, this: Expr<Self::Type>) -> Expr<ClockDomain>; | ||||
| 
 | ||||
|  | @ -266,26 +268,11 @@ impl UnitTrait for DynUnit { | |||
|         self.unit.module() | ||||
|     } | ||||
| 
 | ||||
|     fn input_insn(&self, this: Expr<Self::Type>) -> Expr<ReadyValid<Self::MOp>> { | ||||
|         self.unit.input_insn(this) | ||||
|     } | ||||
| 
 | ||||
|     fn cancel_input(&self, this: Expr<Self::Type>) -> Expr<ReadyValid<UnitCancelInput<DynSize>>> { | ||||
|         self.unit.cancel_input(this) | ||||
|     } | ||||
| 
 | ||||
|     fn unit_forwarding_info( | ||||
|     fn unit_to_reg_alloc( | ||||
|         &self, | ||||
|         this: Expr<Self::Type>, | ||||
|     ) -> Expr<UnitForwardingInfo<DynSize, DynSize, DynSize>> { | ||||
|         self.unit.unit_forwarding_info(this) | ||||
|     } | ||||
| 
 | ||||
|     fn output( | ||||
|         &self, | ||||
|         this: Expr<Self::Type>, | ||||
|     ) -> Expr<ReadyValid<UnitOutput<DynSize, Self::ExtraOut>>> { | ||||
|         self.unit.output(this) | ||||
|     ) -> Expr<UnitToRegAlloc<Self::MOp, Self::ExtraOut, DynSize, DynSize, DynSize>> { | ||||
|         self.unit.unit_to_reg_alloc(this) | ||||
|     } | ||||
| 
 | ||||
|     fn cd(&self, this: Expr<Self::Type>) -> Expr<ClockDomain> { | ||||
|  | @ -332,26 +319,13 @@ impl<T: UnitTrait + Clone + std::hash::Hash + Eq> UnitTrait for DynUnitWrapper<T | |||
|         self.0.module().canonical().intern_sized() | ||||
|     } | ||||
| 
 | ||||
|     fn input_insn(&self, this: Expr<Self::Type>) -> Expr<ReadyValid<Self::MOp>> { | ||||
|         Expr::from_bundle(Expr::as_bundle(self.0.input_insn(Expr::from_bundle(this)))) | ||||
|     } | ||||
| 
 | ||||
|     fn cancel_input(&self, this: Expr<Self::Type>) -> Expr<ReadyValid<UnitCancelInput<DynSize>>> { | ||||
|         self.0.cancel_input(Expr::from_bundle(this)) | ||||
|     } | ||||
| 
 | ||||
|     fn unit_forwarding_info( | ||||
|     fn unit_to_reg_alloc( | ||||
|         &self, | ||||
|         this: Expr<Self::Type>, | ||||
|     ) -> Expr<UnitForwardingInfo<DynSize, DynSize, DynSize>> { | ||||
|         self.0.unit_forwarding_info(Expr::from_bundle(this)) | ||||
|     } | ||||
| 
 | ||||
|     fn output( | ||||
|         &self, | ||||
|         this: Expr<Self::Type>, | ||||
|     ) -> Expr<ReadyValid<UnitOutput<DynSize, Self::ExtraOut>>> { | ||||
|         Expr::from_bundle(Expr::as_bundle(self.0.output(Expr::from_bundle(this)))) | ||||
|     ) -> Expr<UnitToRegAlloc<Self::MOp, Self::ExtraOut, DynSize, DynSize, DynSize>> { | ||||
|         Expr::from_bundle(Expr::as_bundle( | ||||
|             self.0.unit_to_reg_alloc(Expr::from_bundle(this)), | ||||
|         )) | ||||
|     } | ||||
| 
 | ||||
|     fn cd(&self, this: Expr<Self::Type>) -> Expr<ClockDomain> { | ||||
|  |  | |||
|  | @ -5,14 +5,13 @@ use crate::{ | |||
|     config::CpuConfig, | ||||
|     instruction::{AluBranchMOp, UnitOutRegNum}, | ||||
|     unit::{ | ||||
|         unit_base::{unit_base, UnitForwardingInfo}, | ||||
|         DynUnit, DynUnitWrapper, UnitCancelInput, UnitKind, UnitMOp, UnitOutput, UnitTrait, | ||||
|         unit_base::{unit_base, UnitToRegAlloc}, | ||||
|         DynUnit, DynUnitWrapper, UnitKind, UnitMOp, UnitTrait, | ||||
|     }, | ||||
| }; | ||||
| use fayalite::{ | ||||
|     intern::{Intern, Interned}, | ||||
|     prelude::*, | ||||
|     util::ready_valid::ReadyValid, | ||||
| }; | ||||
| 
 | ||||
| #[hdl_module] | ||||
|  | @ -20,30 +19,30 @@ pub fn alu_branch(config: &CpuConfig, unit_index: usize) { | |||
|     #[hdl] | ||||
|     let cd: ClockDomain = m.input(); | ||||
|     #[hdl] | ||||
|     let input_insn: ReadyValid<AluBranchMOp<UnitOutRegNum<DynSize>, DynSize>> = | ||||
|         m.input(ReadyValid[AluBranchMOp[config.unit_out_reg_num()][config.p_reg_num_width()]]); | ||||
|     #[hdl] | ||||
|     let unit_forwarding_info: UnitForwardingInfo<DynSize, DynSize, DynSize> = | ||||
|         m.input(config.unit_forwarding_info()); | ||||
|     #[hdl] | ||||
|     let cancel_input: ReadyValid<UnitCancelInput<DynSize>> = | ||||
|         m.input(ReadyValid[config.unit_cancel_input()]); | ||||
|     #[hdl] | ||||
|     let output: ReadyValid<UnitOutput<DynSize, ()>> = | ||||
|         m.output(ReadyValid[UnitOutput[config.out_reg_num_width][()]]); | ||||
|     let unit_to_reg_alloc: UnitToRegAlloc< | ||||
|         AluBranchMOp<UnitOutRegNum<DynSize>, DynSize>, | ||||
|         (), | ||||
|         DynSize, | ||||
|         DynSize, | ||||
|         DynSize, | ||||
|     > = m.output(config.unit_to_reg_alloc( | ||||
|         AluBranchMOp[config.unit_out_reg_num()][config.p_reg_num_width()], | ||||
|         (), | ||||
|     )); | ||||
|     #[hdl] | ||||
|     let unit_base = instance(unit_base( | ||||
|         config, | ||||
|         unit_index, | ||||
|         Expr::ty(input_insn).data.HdlSome, | ||||
|         Expr::ty(unit_to_reg_alloc).input_insn.data.HdlSome, | ||||
|         (), | ||||
|     )); | ||||
|     connect(unit_base.input_insn, input_insn); | ||||
|     connect(unit_to_reg_alloc, unit_base.unit_to_reg_alloc); | ||||
|     connect(unit_base.cd, cd); | ||||
|     connect(unit_base.unit_forwarding_info, unit_forwarding_info); | ||||
|     connect(unit_base.cancel_input, cancel_input); | ||||
|     // TODO: finish
 | ||||
|     connect(unit_base.ready_mop.ready, true); | ||||
|     connect(output.data, Expr::ty(output.data).HdlNone()); | ||||
|     connect(unit_base.execute_start.ready, true); // TODO: finish
 | ||||
|     connect( | ||||
|         unit_base.execute_end, | ||||
|         Expr::ty(unit_base.execute_end).HdlNone(), | ||||
|     ); // TODO: finish
 | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | ||||
|  | @ -75,7 +74,7 @@ impl UnitTrait for AluBranch { | |||
|     } | ||||
| 
 | ||||
|     fn mop_ty(&self) -> Self::MOp { | ||||
|         self.module.io_ty().input_insn.data.HdlSome | ||||
|         self.module.io_ty().unit_to_reg_alloc.mop_ty() | ||||
|     } | ||||
| 
 | ||||
|     fn unit_kind(&self) -> UnitKind { | ||||
|  | @ -93,26 +92,11 @@ impl UnitTrait for AluBranch { | |||
|         self.module | ||||
|     } | ||||
| 
 | ||||
|     fn input_insn(&self, this: Expr<Self::Type>) -> Expr<ReadyValid<Self::MOp>> { | ||||
|         this.input_insn | ||||
|     } | ||||
| 
 | ||||
|     fn cancel_input(&self, this: Expr<Self::Type>) -> Expr<ReadyValid<UnitCancelInput<DynSize>>> { | ||||
|         this.cancel_input | ||||
|     } | ||||
| 
 | ||||
|     fn unit_forwarding_info( | ||||
|     fn unit_to_reg_alloc( | ||||
|         &self, | ||||
|         this: Expr<Self::Type>, | ||||
|     ) -> Expr<UnitForwardingInfo<DynSize, DynSize, DynSize>> { | ||||
|         this.unit_forwarding_info | ||||
|     } | ||||
| 
 | ||||
|     fn output( | ||||
|         &self, | ||||
|         this: Expr<Self::Type>, | ||||
|     ) -> Expr<ReadyValid<UnitOutput<DynSize, Self::ExtraOut>>> { | ||||
|         this.output | ||||
|     ) -> Expr<UnitToRegAlloc<Self::MOp, Self::ExtraOut, DynSize, DynSize, DynSize>> { | ||||
|         this.unit_to_reg_alloc | ||||
|     } | ||||
| 
 | ||||
|     fn cd(&self, this: Expr<Self::Type>) -> Expr<ClockDomain> { | ||||
|  |  | |||
|  | @ -3,9 +3,9 @@ | |||
| 
 | ||||
| use crate::{ | ||||
|     config::CpuConfig, | ||||
|     instruction::{MOpTrait, UnitOutRegNum, COMMON_MOP_SRC_LEN}, | ||||
|     instruction::{MOpTrait, PRegNum, UnitOutRegNum, COMMON_MOP_SRC_LEN}, | ||||
|     register::PRegValue, | ||||
|     unit::{UnitCancelInput, UnitOutputWrite}, | ||||
|     unit::{UnitCancelInput, UnitOutput, UnitOutputWrite}, | ||||
|     util::tree_reduce::tree_reduce, | ||||
| }; | ||||
| use fayalite::{module::wire_with_loc, prelude::*, ty::StaticType, util::ready_valid::ReadyValid}; | ||||
|  | @ -18,113 +18,439 @@ pub struct UnitForwardingInfo<UnitNumWidth: Size, OutRegNumWidth: Size, UnitCoun | |||
| } | ||||
| 
 | ||||
| #[hdl] | ||||
| pub struct ReadyMOp<MOp: Type + MOpTrait<DestReg = UnitOutRegNum<DynSize>>> { | ||||
| 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_insn: ReadyValid<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_insn.data.HdlSome | ||||
|     } | ||||
|     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 src_values: Array<PRegValue, { COMMON_MOP_SRC_LEN }>, | ||||
| } | ||||
| 
 | ||||
| #[hdl] | ||||
| struct InFlightOp<MOp: Type + MOpTrait<DestReg = UnitOutRegNum<DynSize>>> { | ||||
|     pub mop: MOp, | ||||
|     pub src_values: Array<HdlOption<PRegValue>, { COMMON_MOP_SRC_LEN }>, | ||||
| pub struct ExecuteEnd<OutRegNumWidth: Size, ExtraOut> { | ||||
|     pub unit_output: UnitOutput<OutRegNumWidth, ExtraOut>, | ||||
| } | ||||
| 
 | ||||
| #[hdl] | ||||
| enum InFlightOpState { | ||||
|     Ready, | ||||
|     Running, | ||||
|     CanceledAndRunning, | ||||
| } | ||||
| 
 | ||||
| impl InFlightOpState { | ||||
|     fn ready_next_state(canceling: bool, starting: bool, ending: bool) -> Expr<HdlOption<Self>> { | ||||
|         match (canceling, starting, ending) { | ||||
|             (false, false, _) => HdlSome(InFlightOpState.Ready()), | ||||
|             (false, true, false) => HdlSome(InFlightOpState.Running()), | ||||
|             (false, true, true) => HdlNone(), | ||||
|             (true, false, _) => HdlNone(), | ||||
|             (true, true, false) => HdlSome(InFlightOpState.CanceledAndRunning()), | ||||
|             (true, true, true) => HdlNone(), | ||||
|         } | ||||
|     } | ||||
|     fn running_next_state(canceling: bool, _starting: bool, ending: bool) -> Expr<HdlOption<Self>> { | ||||
|         match (canceling, ending) { | ||||
|             (false, false) => HdlSome(InFlightOpState.Running()), | ||||
|             (false, true) => HdlNone(), | ||||
|             (true, false) => HdlSome(InFlightOpState.CanceledAndRunning()), | ||||
|             (true, true) => HdlNone(), | ||||
|         } | ||||
|     } | ||||
|     fn canceled_and_running_next_state( | ||||
|         _canceling: bool, | ||||
|         _starting: bool, | ||||
|         ending: bool, | ||||
|     ) -> Expr<HdlOption<Self>> { | ||||
|         if ending { | ||||
|             HdlNone() | ||||
|         } else { | ||||
|             HdlSome(InFlightOpState.CanceledAndRunning()) | ||||
|         } | ||||
|     } | ||||
|     /// FIXME: this is working around #[hdl] match not supporting matching values inside structs yet
 | ||||
|     #[hdl] | ||||
|     fn connect_next_state( | ||||
|         canceling: Expr<Bool>, | ||||
|         starting: Expr<Bool>, | ||||
|         ending: Expr<Bool>, | ||||
|         next_state_fn: fn(canceling: bool, starting: bool, ending: bool) -> Expr<HdlOption<Self>>, | ||||
|         next_state: Expr<HdlOption<Self>>, | ||||
|     ) { | ||||
|         #[hdl] | ||||
|         fn recurse<const N: usize>( | ||||
|             exprs: &[Expr<Bool>; N], | ||||
|             bools: &mut [bool; N], | ||||
|             f: &mut impl FnMut(&[bool; N]), | ||||
|             arg_index: usize, | ||||
|         ) { | ||||
|             if arg_index < N { | ||||
|                 #[hdl] | ||||
|                 if exprs[arg_index] { | ||||
|                     bools[arg_index] = true; | ||||
|                     recurse(exprs, bools, f, arg_index + 1); | ||||
|                 } else { | ||||
|                     bools[arg_index] = false; | ||||
|                     recurse(exprs, bools, f, arg_index + 1); | ||||
|                 } | ||||
|             } else { | ||||
|                 f(bools); | ||||
|             } | ||||
|         } | ||||
|         recurse( | ||||
|             &[canceling, starting, ending], | ||||
|             &mut [false; 3], | ||||
|             &mut |&[canceling, starting, ending]| { | ||||
|                 connect(next_state, next_state_fn(canceling, starting, ending)) | ||||
|             }, | ||||
|             0, | ||||
|         ); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[hdl] | ||||
| struct InFlightOp<MOp: Type> { | ||||
|     state: InFlightOpState, | ||||
|     mop: MOp, | ||||
|     src_ready_flags: Array<Bool, { COMMON_MOP_SRC_LEN }>, | ||||
| } | ||||
| 
 | ||||
| #[hdl] | ||||
| struct InFlightOpsSummary<OpIndexWidth: Size> { | ||||
|     empty_op_index: HdlOption<UIntType<OpIndexWidth>>, | ||||
|     ready_op_index: HdlOption<UIntType<OpIndexWidth>>, | ||||
| } | ||||
| 
 | ||||
| impl<OpIndexWidth: Size> InFlightOpsSummary<OpIndexWidth> { | ||||
|     #[hdl] | ||||
|     fn new<MOp: Type>( | ||||
|         op_index: usize, | ||||
|         op_index_ty: UIntType<OpIndexWidth>, | ||||
|         in_flight_op: impl ToExpr<Type = HdlOption<InFlightOp<MOp>>>, | ||||
|     ) -> Expr<Self> { | ||||
|         let empty_op_index = wire_with_loc( | ||||
|             &format!("empty_op_index_{op_index}"), | ||||
|             SourceLocation::caller(), | ||||
|             HdlOption[op_index_ty], | ||||
|         ); | ||||
|         connect(empty_op_index, HdlOption[op_index_ty].HdlNone()); | ||||
|         let ready_op_index = wire_with_loc( | ||||
|             &format!("ready_op_index_{op_index}"), | ||||
|             SourceLocation::caller(), | ||||
|             HdlOption[op_index_ty], | ||||
|         ); | ||||
|         connect(ready_op_index, HdlOption[op_index_ty].HdlNone()); | ||||
|         #[hdl] | ||||
|         if let HdlSome(in_flight_op) = in_flight_op { | ||||
|             #[hdl] | ||||
|             let InFlightOp::<_> { | ||||
|                 state, | ||||
|                 mop: _, | ||||
|                 src_ready_flags, | ||||
|             } = in_flight_op; | ||||
|             connect(ready_op_index, HdlOption[op_index_ty].HdlNone()); | ||||
|             #[hdl] | ||||
|             match state { | ||||
|                 InFlightOpState::Ready => | ||||
|                 { | ||||
|                     #[hdl] | ||||
|                     if src_ready_flags.cmp_eq([true; COMMON_MOP_SRC_LEN]) { | ||||
|                         connect(ready_op_index, HdlSome(op_index.cast_to(op_index_ty))); | ||||
|                     } | ||||
|                 } | ||||
|                 InFlightOpState::CanceledAndRunning | InFlightOpState::Running => {} | ||||
|             } | ||||
|         } else { | ||||
|             connect(empty_op_index, HdlSome(op_index.cast_to(op_index_ty))); | ||||
|         } | ||||
|         #[hdl] | ||||
|         InFlightOpsSummary::<_> { | ||||
|             empty_op_index, | ||||
|             ready_op_index, | ||||
|         } | ||||
|     } | ||||
|     #[hdl] | ||||
|     fn combine(l: impl ToExpr<Type = Self>, r: impl ToExpr<Type = Self>) -> Expr<Self> { | ||||
|         let l = l.to_expr(); | ||||
|         let r = r.to_expr(); | ||||
|         #[hdl] | ||||
|         InFlightOpsSummary::<_> { | ||||
|             empty_op_index: HdlOption::or(l.empty_op_index, r.empty_op_index), | ||||
|             ready_op_index: HdlOption::or(l.ready_op_index, r.ready_op_index), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl InFlightOpsSummary<DynSize> { | ||||
|     fn summarize<MOp: Type, MaxInFlight: Size>( | ||||
|         in_flight_ops: impl ToExpr<Type = ArrayType<HdlOption<InFlightOp<MOp>>, MaxInFlight>>, | ||||
|     ) -> Expr<Self> { | ||||
|         let in_flight_ops = in_flight_ops.to_expr(); | ||||
|         let max_in_flight = Expr::ty(in_flight_ops).len(); | ||||
|         let index_range = 0..max_in_flight; | ||||
|         let index_ty = UInt::range(index_range.clone()); | ||||
|         tree_reduce( | ||||
|             index_range.map(|i| Self::new(i, index_ty, in_flight_ops[i])), | ||||
|             Self::combine, | ||||
|         ) | ||||
|         .expect("in_flight_ops is known to have len > 0") | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[hdl_module] | ||||
| pub fn unit_base<MOp: Type + MOpTrait<DestReg = UnitOutRegNum<DynSize>>>( | ||||
| pub fn unit_base< | ||||
|     MOp: Type + MOpTrait<DestReg = UnitOutRegNum<DynSize>, SrcRegWidth = DynSize>, | ||||
|     ExtraOut: Type, | ||||
| >( | ||||
|     config: &CpuConfig, | ||||
|     unit_index: usize, | ||||
|     mop_ty: MOp, | ||||
|     extra_out_ty: ExtraOut, | ||||
| ) { | ||||
|     #[hdl] | ||||
|     let cd: ClockDomain = m.input(); | ||||
|     #[hdl] | ||||
|     let unit_forwarding_info: UnitForwardingInfo<DynSize, DynSize, DynSize> = | ||||
|         m.input(config.unit_forwarding_info()); | ||||
|     let unit_to_reg_alloc: UnitToRegAlloc<MOp, ExtraOut, DynSize, DynSize, DynSize> = | ||||
|         m.output(config.unit_to_reg_alloc(mop_ty, extra_out_ty)); | ||||
|     #[hdl] | ||||
|     let input_insn: ReadyValid<MOp> = m.input(ReadyValid[mop_ty]); | ||||
|     connect(input_insn.ready, false); | ||||
|     let execute_start: ReadyValid<ExecuteStart<MOp>> = m.output(ReadyValid[ExecuteStart[mop_ty]]); | ||||
|     #[hdl] | ||||
|     let cancel_input: ReadyValid<UnitCancelInput<DynSize>> = | ||||
|         m.input(ReadyValid[config.unit_cancel_input()]); | ||||
|     connect(cancel_input.ready, true); | ||||
|     #[hdl] | ||||
|     let ready_mop: ReadyValid<ReadyMOp<MOp>> = m.output(ReadyValid[ReadyMOp[mop_ty]]); | ||||
|     connect(ready_mop.data, Expr::ty(ready_mop.data).HdlNone()); | ||||
|     let execute_end: HdlOption<ExecuteEnd<DynSize, ExtraOut>> = | ||||
|         m.input(HdlOption[ExecuteEnd[config.out_reg_num_width][extra_out_ty]]); | ||||
| 
 | ||||
|     connect(execute_start.data, Expr::ty(execute_start).data.HdlNone()); | ||||
| 
 | ||||
|     let max_in_flight = config.unit_max_in_flight(unit_index).get(); | ||||
|     let in_flight_op_ty = InFlightOp[mop_ty]; | ||||
|     #[hdl] | ||||
|     let in_flight_ops = reg_builder().clock_domain(cd).reset(repeat( | ||||
|         HdlOption[InFlightOp[mop_ty]].HdlNone(), | ||||
|         max_in_flight, | ||||
|     )); | ||||
|     let in_flight_op_index_ty = UInt::range(0..max_in_flight); | ||||
|     let in_flight_ops = reg_builder() | ||||
|         .clock_domain(cd) | ||||
|         .reset(repeat(HdlOption[in_flight_op_ty].HdlNone(), max_in_flight)); | ||||
| 
 | ||||
|     let in_flight_ops_summary_value = InFlightOpsSummary::summarize(in_flight_ops); | ||||
|     #[hdl] | ||||
|     let input_index = wire(HdlOption[in_flight_op_index_ty]); | ||||
|     let in_flight_ops_summary = wire(Expr::ty(in_flight_ops_summary_value)); | ||||
|     connect(in_flight_ops_summary, in_flight_ops_summary_value); | ||||
| 
 | ||||
|     connect( | ||||
|         input_index, | ||||
|         tree_reduce( | ||||
|             (0..max_in_flight).map(|i| -> Expr<HdlOption<UInt>> { | ||||
|                 HdlOption::map(in_flight_ops[i], |_| i.cast_to(in_flight_op_index_ty)) | ||||
|             }), | ||||
|             HdlOption::or, | ||||
|         ) | ||||
|         .expect("max_in_flight is known to be non-zero"), | ||||
|         unit_to_reg_alloc.input_insn.ready, | ||||
|         HdlOption::is_some(in_flight_ops_summary.empty_op_index), | ||||
|     ); | ||||
|     // TODO: connect(execute_start.data, <read_regs>(in_flight_ops_summary.ready_op_index));
 | ||||
| 
 | ||||
|     connect( | ||||
|         unit_to_reg_alloc.output, | ||||
|         Expr::ty(unit_to_reg_alloc.output).HdlNone(), | ||||
|     ); // TODO: finish
 | ||||
| 
 | ||||
|     #[hdl] | ||||
|     let input_in_flight_op = wire(HdlOption[InFlightOp[mop_ty]]); | ||||
|     connect(input_in_flight_op, Expr::ty(input_in_flight_op).HdlNone()); | ||||
|     let input_in_flight_op = wire(HdlOption[in_flight_op_ty]); | ||||
|     connect(input_in_flight_op, HdlOption[in_flight_op_ty].HdlNone()); | ||||
|     #[hdl] | ||||
|     if let HdlSome(mop) = ReadyValid::firing_data(input_insn) { | ||||
|         let src_values = wire_with_loc( | ||||
|             "input_in_flight_op_src_values", | ||||
|     if let HdlSome(mop) = ReadyValid::firing_data(unit_to_reg_alloc.input_insn) { | ||||
|         #[hdl] | ||||
|         let input_mop_src_regs = wire(mop_ty.src_regs_ty()); | ||||
|         connect( | ||||
|             input_mop_src_regs, | ||||
|             repeat(config.p_reg_num().const_zero().cast_to_bits(), ConstUsize), | ||||
|         ); | ||||
|         MOp::connect_src_regs(mop, input_mop_src_regs); | ||||
|         let src_ready_flags = wire_with_loc( | ||||
|             "input_in_flight_op_src_ready_flags", | ||||
|             SourceLocation::caller(), | ||||
|             StaticType::TYPE, | ||||
|         ); | ||||
|         for src_index in 0..COMMON_MOP_SRC_LEN { | ||||
|             connect( | ||||
|             src_values, | ||||
|             [HdlSome(PRegValue::zeroed()); COMMON_MOP_SRC_LEN], | ||||
|         ); | ||||
|         MOp::for_each_src_reg(mop, &mut |src_reg, src_index| { | ||||
|             #[hdl] | ||||
|             if config | ||||
|                 src_ready_flags[src_index], | ||||
|                 config | ||||
|                     .p_reg_num() | ||||
|                     .const_zero() | ||||
|                     .cast_to_bits() | ||||
|                 .cmp_ne(src_reg) | ||||
|             { | ||||
|                 connect(src_values[src_index], HdlNone()); | ||||
|                     .cmp_eq(input_mop_src_regs[src_index]), | ||||
|             ); | ||||
|         } | ||||
|         }); | ||||
|         #[hdl] | ||||
|         if unit_to_reg_alloc.cancel_input.cmp_ne(HdlSome( | ||||
|             #[hdl] | ||||
|             UnitCancelInput::<_> { | ||||
|                 which: MOp::dest_reg(mop), | ||||
|             }, | ||||
|         )) { | ||||
|             connect( | ||||
|                 input_in_flight_op, | ||||
|                 HdlSome( | ||||
|                     #[hdl] | ||||
|                 InFlightOp::<_> { mop, src_values }, | ||||
|                     InFlightOp::<_> { | ||||
|                         state: InFlightOpState.Ready(), | ||||
|                         mop, | ||||
|                         src_ready_flags, | ||||
|                     }, | ||||
|                 ), | ||||
|             ); | ||||
|         } | ||||
|         #[hdl] | ||||
|         if let HdlSome(empty_op_index) = in_flight_ops_summary.empty_op_index { | ||||
|             connect(in_flight_ops[empty_op_index], input_in_flight_op); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     #[hdl] | ||||
|     let in_flight_op_next_state = wire(Array[HdlOption[InFlightOpState]][max_in_flight]); | ||||
|     #[hdl] | ||||
|     let in_flight_op_next_src_ready_flags = | ||||
|         wire(Array[in_flight_op_ty.src_ready_flags][max_in_flight]); | ||||
|     #[hdl] | ||||
|     let in_flight_op_canceling = wire(Array[Bool][max_in_flight]); | ||||
|     #[hdl] | ||||
|     let in_flight_op_execute_starting = wire(Array[Bool][max_in_flight]); | ||||
|     #[hdl] | ||||
|     let in_flight_op_execute_ending = wire(Array[Bool][max_in_flight]); | ||||
|     for in_flight_op_index in 0..max_in_flight { | ||||
|         connect( | ||||
|             in_flight_op_next_src_ready_flags[in_flight_op_index], | ||||
|             [false; COMMON_MOP_SRC_LEN], | ||||
|         ); | ||||
|         connect(in_flight_op_canceling[in_flight_op_index], false); | ||||
|         connect(in_flight_op_execute_starting[in_flight_op_index], false); | ||||
|         connect(in_flight_op_execute_ending[in_flight_op_index], false); | ||||
|         #[hdl] | ||||
|         if let HdlSome(in_flight_op) = in_flight_ops[in_flight_op_index] { | ||||
|             #[hdl] | ||||
|             if let HdlSome(cancel_input) = ReadyValid::firing_data(cancel_input) { | ||||
|                 #[hdl] | ||||
|                 let UnitCancelInput::<_> { which } = cancel_input; | ||||
|                 #[hdl] | ||||
|                 if which.value.cmp_eq(MOp::dest_reg(in_flight_op.mop).value) { | ||||
|                     // TODO: if it needs extra time to cancel (e.g. still in pipeline), handle that here
 | ||||
|             let InFlightOp::<_> { | ||||
|                 state, | ||||
|                 mop, | ||||
|                 src_ready_flags, | ||||
|             } = in_flight_op; | ||||
|             let which = MOp::dest_reg(mop); | ||||
|             let src_regs = wire_with_loc( | ||||
|                 &format!("in_flight_op_src_regs_{in_flight_op_index}"), | ||||
|                 SourceLocation::caller(), | ||||
|                 mop_ty.src_regs_ty(), | ||||
|             ); | ||||
|             connect( | ||||
|                         in_flight_ops[in_flight_op_index], | ||||
|                         HdlOption[InFlightOp[mop_ty]].HdlNone(), | ||||
|                 src_regs, | ||||
|                 repeat(config.p_reg_num().const_zero().cast_to_bits(), ConstUsize), | ||||
|             ); | ||||
|             MOp::connect_src_regs(mop, src_regs); | ||||
| 
 | ||||
|             connect( | ||||
|                 in_flight_op_next_src_ready_flags[in_flight_op_index], | ||||
|                 src_ready_flags, | ||||
|             ); | ||||
|             let unit_output_writes = unit_to_reg_alloc.unit_forwarding_info.unit_output_writes; | ||||
|             for unit_index in 0..Expr::ty(unit_output_writes).len() { | ||||
|                 #[hdl] | ||||
|                 if let HdlSome(unit_output_write) = 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, | ||||
|                     }; | ||||
|                     for src_index in 0..COMMON_MOP_SRC_LEN { | ||||
|                         #[hdl] | ||||
|                         if p_reg_num.cast_to_bits().cmp_eq(src_regs[src_index]) { | ||||
|                             connect( | ||||
|                                 in_flight_op_next_src_ready_flags[in_flight_op_index][src_index], | ||||
|                                 true, | ||||
|                             ); | ||||
|                         } | ||||
|                     } | ||||
|             // TODO: finish
 | ||||
|         } else if let HdlSome(input_index) = input_index { | ||||
|             connect(input_insn.ready, true); | ||||
|             #[hdl] | ||||
|             if input_index.cmp_eq(in_flight_op_index) { | ||||
|                 connect(in_flight_ops[in_flight_op_index], input_in_flight_op); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             connect( | ||||
|                 in_flight_op_canceling[in_flight_op_index], | ||||
|                 unit_to_reg_alloc.cancel_input.cmp_eq(HdlSome( | ||||
|                     #[hdl] | ||||
|                     UnitCancelInput::<_> { which }, | ||||
|                 )), | ||||
|             ); | ||||
| 
 | ||||
|             #[hdl] | ||||
|             if let HdlSome(execute_end) = execute_end { | ||||
|                 #[hdl] | ||||
|                 let ExecuteEnd::<_, _> { unit_output } = execute_end; | ||||
|                 #[hdl] | ||||
|                 if which.cmp_eq(unit_output.which) { | ||||
|                     connect(in_flight_op_execute_ending[in_flight_op_index], true); | ||||
|                 } | ||||
|             } | ||||
|             #[hdl] | ||||
|             if let HdlSome(execute_start) = ReadyValid::firing_data(execute_start) { | ||||
|                 #[hdl] | ||||
|                 if which.cmp_eq(MOp::dest_reg(execute_start.mop)) { | ||||
|                     connect(in_flight_op_execute_starting[in_flight_op_index], true); | ||||
|                 } | ||||
|             } | ||||
|             let connect_next_state = |f| { | ||||
|                 InFlightOpState::connect_next_state( | ||||
|                     in_flight_op_canceling[in_flight_op_index], | ||||
|                     in_flight_op_execute_starting[in_flight_op_index], | ||||
|                     in_flight_op_execute_ending[in_flight_op_index], | ||||
|                     f, | ||||
|                     in_flight_op_next_state[in_flight_op_index], | ||||
|                 ); | ||||
|             }; | ||||
|             #[hdl] | ||||
|             match state { | ||||
|                 InFlightOpState::Ready => connect_next_state(InFlightOpState::ready_next_state), | ||||
|                 InFlightOpState::Running => connect_next_state(InFlightOpState::running_next_state), | ||||
|                 InFlightOpState::CanceledAndRunning => { | ||||
|                     connect_next_state(InFlightOpState::canceled_and_running_next_state); | ||||
|                 } | ||||
|             } | ||||
|             #[hdl] | ||||
|             if let HdlSome(state) = in_flight_op_next_state[in_flight_op_index] { | ||||
|                 connect( | ||||
|                     in_flight_ops[in_flight_op_index], | ||||
|                     HdlSome( | ||||
|                         #[hdl] | ||||
|                         InFlightOp::<_> { | ||||
|                             state, | ||||
|                             mop, | ||||
|                             src_ready_flags, | ||||
|                         }, | ||||
|                     ), | ||||
|                 ); | ||||
|             } else { | ||||
|                 connect( | ||||
|                     in_flight_ops[in_flight_op_index], | ||||
|                     HdlOption[in_flight_op_ty].HdlNone(), | ||||
|                 ); | ||||
|             } | ||||
|         } else { | ||||
|             connect(in_flight_op_next_state[in_flight_op_index], HdlNone()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue