Compare commits

...

10 commits

17 changed files with 188979 additions and 204028 deletions

View file

@ -16,4 +16,5 @@ jobs:
- uses: https://git.libre-chip.org/mirrors/rust-cache@v2
with:
save-if: ${{ github.ref == 'refs/heads/master' }}
- run: rustup override set 1.93.0
- run: cargo test

13
Cargo.lock generated
View file

@ -388,7 +388,7 @@ checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
[[package]]
name = "fayalite"
version = "0.3.0"
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#c97b44d9d646a4aa64fcc046538fc2354bb708ee"
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#c632e5d570d4763e8e18d764e95b7a9e515ebf99"
dependencies = [
"base64",
"bitvec",
@ -403,6 +403,7 @@ dependencies = [
"jobslot",
"num-bigint",
"num-traits",
"once_cell",
"ordered-float",
"petgraph",
"serde",
@ -415,7 +416,7 @@ dependencies = [
[[package]]
name = "fayalite-proc-macros"
version = "0.3.0"
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#c97b44d9d646a4aa64fcc046538fc2354bb708ee"
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#c632e5d570d4763e8e18d764e95b7a9e515ebf99"
dependencies = [
"fayalite-proc-macros-impl",
]
@ -423,7 +424,7 @@ dependencies = [
[[package]]
name = "fayalite-proc-macros-impl"
version = "0.3.0"
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#c97b44d9d646a4aa64fcc046538fc2354bb708ee"
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#c632e5d570d4763e8e18d764e95b7a9e515ebf99"
dependencies = [
"base16ct 0.2.0",
"num-bigint",
@ -438,7 +439,7 @@ dependencies = [
[[package]]
name = "fayalite-visit-gen"
version = "0.3.0"
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#c97b44d9d646a4aa64fcc046538fc2354bb708ee"
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#c632e5d570d4763e8e18d764e95b7a9e515ebf99"
dependencies = [
"indexmap",
"prettyplease",
@ -739,9 +740,9 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.20.2"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "ordered-float"

View file

@ -11,7 +11,7 @@ edition = "2024"
repository = ""
keywords = []
categories = []
rust-version = "1.89.0"
rust-version = "1.93.0"
[workspace.dependencies]
base16ct = "1.0.0"

View file

@ -37,6 +37,8 @@ pub struct CpuConfig {
pub max_branches_per_fetch: NonZeroUsize,
pub max_fetches_in_flight: NonZeroUsize,
pub log2_fetch_width_in_bytes: u8,
pub log2_cache_line_size_in_bytes: u8,
pub l1_i_cache_line_count: NonZeroUsize,
/// default value for [`UnitConfig::max_in_flight`]
pub default_unit_max_in_flight: NonZeroUsize,
pub rob_size: NonZeroUsize,
@ -63,6 +65,13 @@ impl CpuConfig {
v
};
pub const DEFAULT_LOG2_FETCH_WIDTH_IN_BYTES: u8 = 3;
pub const DEFAULT_LOG2_CACHE_LINE_SIZE_IN_BYTES: u8 = 6;
pub const DEFAULT_L1_I_CACHE_LINE_COUNT: NonZeroUsize = {
let Some(v) = NonZeroUsize::new(256) else {
unreachable!();
};
v
};
pub const DEFAULT_UNIT_MAX_IN_FLIGHT: NonZeroUsize = {
let Some(v) = NonZeroUsize::new(8) else {
unreachable!();
@ -77,6 +86,8 @@ impl CpuConfig {
max_branches_per_fetch: Self::DEFAULT_MAX_BRANCHES_PER_FETCH,
max_fetches_in_flight: Self::DEFAULT_MAX_FETCHES_IN_FLIGHT,
log2_fetch_width_in_bytes: Self::DEFAULT_LOG2_FETCH_WIDTH_IN_BYTES,
log2_cache_line_size_in_bytes: Self::DEFAULT_LOG2_CACHE_LINE_SIZE_IN_BYTES,
l1_i_cache_line_count: Self::DEFAULT_L1_I_CACHE_LINE_COUNT,
default_unit_max_in_flight: Self::DEFAULT_UNIT_MAX_IN_FLIGHT,
rob_size,
}
@ -141,6 +152,17 @@ impl CpuConfig {
.checked_shl(self.log2_fetch_width_in_bytes.into())
.expect("log2_fetch_width_in_bytes is too big")
}
pub fn cache_line_size_in_bytes(&self) -> usize {
1usize
.checked_shl(self.log2_cache_line_size_in_bytes.into())
.expect("log2_cache_line_size_in_bytes is too big")
}
pub fn l1_i_cache_size_in_bytes(&self) -> usize {
self.l1_i_cache_line_count
.get()
.checked_mul(self.cache_line_size_in_bytes())
.expect("L1 I-Cache is too big")
}
}
#[hdl(get(|c| c.fetch_width.get()))]
@ -161,6 +183,18 @@ pub type CpuConfigLog2FetchWidthInBytes<C: PhantomConstGet<CpuConfig>> = DynSize
#[hdl(get(|c| c.fetch_width_in_bytes()))]
pub type CpuConfigFetchWidthInBytes<C: PhantomConstGet<CpuConfig>> = DynSize;
#[hdl(get(|c| c.log2_cache_line_size_in_bytes.into()))]
pub type CpuConfigLog2CacheLineSizeInBytes<C: PhantomConstGet<CpuConfig>> = DynSize;
#[hdl(get(|c| c.cache_line_size_in_bytes()))]
pub type CpuConfigCacheLineSizeInBytes<C: PhantomConstGet<CpuConfig>> = DynSize;
#[hdl(get(|c| c.l1_i_cache_line_count.get()))]
pub type CpuConfigL1ICacheLineCount<C: PhantomConstGet<CpuConfig>> = DynSize;
#[hdl(get(|c| c.l1_i_cache_size_in_bytes()))]
pub type CpuConfigL1ICacheSizeInBytes<C: PhantomConstGet<CpuConfig>> = DynSize;
#[hdl(get(|c| c.rob_size.get()))]
pub type CpuConfigRobSize<C: PhantomConstGet<CpuConfig>> = DynSize;

View file

@ -6,7 +6,9 @@ use crate::{
instruction::{
AddSubMOp, BranchMOp, CompareMOp, CompareMode, ConditionMode, LoadMOp, LoadStoreConversion,
LoadStoreWidth, LogicalFlagsMOp, LogicalFlagsMOpImm, LogicalMOp, Lut4, MOp, MOpDestReg,
MOpRegNum, MoveRegMOp, OutputIntegerMode, StoreMOp,
MOpRegNum, MoveRegMOp, OutputIntegerMode, ReadSpecialMOp, ReadSpecialMOpImm,
ShiftRotateDestLogicOp, ShiftRotateMOp, ShiftRotateMOpImm, ShiftRotateMode, StoreMOp,
power_isa::PowerIsaSprEnum,
},
powerisa_instructions_xml::{
InstructionBitFieldName, InstructionBitFieldsInner, Instructions, TextLineItem,
@ -14,15 +16,16 @@ use crate::{
register::{
PRegFlags, PRegFlagsPowerISA, PRegFlagsPowerISAView, PRegFlagsViewTrait, ViewUnused,
},
util::array_vec::{ArrayVec, Length},
util::{
Rotate,
array_vec::{ArrayVec, Length},
},
};
use fayalite::{
int::{BoolOrIntType, UIntInRange},
module::wire_with_loc,
prelude::*,
ty::StaticType,
use fayalite::{int::UIntInRange, module::wire_with_loc, prelude::*, ty::StaticType};
use std::{
collections::{BTreeMap, HashMap, hash_map::Entry},
ops::RangeInclusive,
};
use std::{collections::BTreeMap, ops::RangeInclusive};
#[rustfmt::skip]
const FP_MNEMONICS: &[&str] = &[
@ -129,7 +132,7 @@ const VSX_MNEMONICS: &[&str] = &[
];
#[derive(Debug)]
struct DecodeState {
struct DecodeState<'a> {
mnemonic: &'static str,
arguments: Option<&'static str>,
conditions: Option<&'static str>,
@ -140,6 +143,7 @@ struct DecodeState {
first_input: Expr<UInt<32>>,
second_input: Expr<HdlOption<UInt<32>>>,
second_input_used: Expr<Bool>,
field_wires: &'a mut HashMap<String, HashMap<Expr<UInt>, Expr<UInt>>>,
}
trait FieldSet {
@ -218,12 +222,28 @@ impl_fields! {
struct FieldDS(SInt<14>);
#[name = "LI"]
struct FieldLI(SInt<24>);
#[name = "MB"]
struct FieldMB(UInt<5>);
#[name = "mb"]
struct FieldMb(UInt<6>);
#[name = "ME"]
struct FieldME(UInt<5>);
#[name = "me"]
struct FieldMe(UInt<6>);
#[name = "SH"]
struct FieldSH(UInt<5>);
#[name = "sh"]
struct FieldSh(UInt<6>);
#[name = "SI"]
struct FieldSI(SInt<16>);
#[name = "si0"]
struct FieldSi0(SInt<18>);
#[name = "si1"]
struct FieldSi1(UInt<16>);
#[name = "spr"]
struct FieldSpr(UInt<10>);
#[name = "tbr"]
struct FieldTbr(UInt<10>);
#[name = "UI"]
struct FieldUI(UInt<16>);
#[name = "d0"]
@ -309,7 +329,7 @@ fn cr_bit_flag_index(
(MOpRegNum::power_isa_cr_reg(field_num), flag_index)
}
impl DecodeState {
impl DecodeState<'_> {
fn form(&self) -> &'static str {
let mut title_words = self
.header
@ -388,6 +408,26 @@ impl DecodeState {
(var.trim(), value.trim())
})
}
fn get_field_wire(&mut self, value: Expr<UInt>, name: &str) -> Expr<UInt> {
let form = self.form();
let width = value.ty().width();
let wire_name =
format!("{form}_{name}_{width}").replace(|c: char| !c.is_ascii_alphanumeric(), "_");
match self
.field_wires
.entry(wire_name.clone())
.or_default()
.entry(value)
{
Entry::Vacant(entry) => {
let wire = wire_with_loc(&wire_name, SourceLocation::caller(), value.ty());
connect(wire, value);
entry.insert(wire);
wire
}
Entry::Occupied(entry) => *entry.get(),
}
}
#[hdl]
fn decode_word(
&mut self,
@ -397,6 +437,20 @@ impl DecodeState {
fields_inner: &'static InstructionBitFieldsInner,
) {
let mut last_start = word.ty().width();
struct FieldPieces {
filled_pieces: Vec<Expr<UInt>>,
piece_count: usize,
}
let mut fields_pieces = BTreeMap::new();
for bit_field in fields_inner.fields() {
fields_pieces
.entry(Self::bit_field_name(bit_field.name()))
.or_insert_with(|| FieldPieces {
filled_pieces: Vec::with_capacity(1),
piece_count: 0,
})
.piece_count += 1;
}
for bit_field in fields_inner.fields().iter().rev() {
let bit_number_text = match bit_field.bit_number().text() {
"3031" => "30 31",
@ -436,6 +490,10 @@ impl DecodeState {
if name.contains(char::is_alphabetic) {
for (cond_name, cond_value) in self.conditions() {
if name == cond_name {
assert_eq!(
fields_pieces[name].piece_count, 1,
"can't apply conditions to a field with more than one piece: name {name:?}",
);
name = cond_value;
break;
}
@ -444,15 +502,25 @@ impl DecodeState {
if name == "any value*" || name.bytes().all(|b| b == b'/') {
// wildcard
} else if name.contains(char::is_alphabetic) {
let wire = wire_with_loc(
&format!("{}_{name}", self.mnemonic)
.replace(|c: char| !c.is_ascii_alphanumeric(), "_"),
SourceLocation::caller(),
field.ty(),
);
connect(wire, field);
if fields.insert(name, wire).is_some() {
panic!("duplicate field name: {name:?}\nheader: {:#?}", self.header);
let Some(field_pieces) = fields_pieces.get_mut(name) else {
unreachable!();
};
field_pieces.filled_pieces.push(field);
if field_pieces.filled_pieces.len() == field_pieces.piece_count {
let filled_pieces = std::mem::take(&mut field_pieces.filled_pieces);
// filled_pieces are in lsb to msb order
let mut filled_pieces = filled_pieces.into_iter();
let Some(mut value) = filled_pieces.next() else {
unreachable!();
};
let mut value_width = value.ty().width();
for next_value in filled_pieces {
value = value | (next_value << value_width);
value_width += next_value.ty().width();
}
value = value.cast_to(UInt[value_width]);
let wire = self.get_field_wire(value, name);
fields.insert(name, wire);
}
} else {
let value: u32 = name.parse().expect("bit field name must have at least one letter, be all `/`, or be a valid decimal number");
@ -476,8 +544,8 @@ impl DecodeState {
let mut f = Some(f);
#[hdl]
#[track_caller]
fn run<FS: FieldSet, F: FnOnce(&mut DecodeState, FS)>(
this: &mut DecodeState,
fn run<'a, FS: FieldSet, F: FnOnce(&mut DecodeState<'a>, FS)>(
this: &mut DecodeState<'a>,
matches: Expr<Bool>,
fields: &mut BTreeMap<&str, Expr<UInt>>,
f: &mut Option<F>,
@ -1856,6 +1924,510 @@ impl DecodeState {
);
});
}
#[hdl]
fn rotate_dest_logic_op(
&mut self,
msb0_mask_begin_: Expr<UInt<6>>,
msb0_mask_end_: Expr<UInt<6>>,
fallback_is_src2: bool,
) -> Expr<HdlOption<ShiftRotateDestLogicOp>> {
#[hdl]
let msb0_mask_begin = wire();
connect(msb0_mask_begin, msb0_mask_begin_);
#[hdl]
let msb0_mask_end = wire();
connect(msb0_mask_end, msb0_mask_end_);
#[hdl]
let rotated_output_start: UInt<6> = wire();
#[hdl]
let rotated_output_len: UInt<6> = wire();
// gives the correct value modulo 2^6 even when `msb0_mask_begin > msb0_mask_end`
connect_any(
rotated_output_len,
msb0_mask_end - msb0_mask_begin + 1_hdl_u6,
);
// account for lsb0 vs. msb0
connect(rotated_output_start, !msb0_mask_end);
#[hdl]
let rotate_dest_logic_op = wire();
#[hdl]
if rotated_output_len.cmp_eq(0_hdl_u6) {
// it's really 64, it wrapped around to 0
connect(rotate_dest_logic_op, HdlNone());
} else {
connect(
rotate_dest_logic_op,
HdlSome(
#[hdl]
ShiftRotateDestLogicOp {
rotated_output_start,
rotated_output_len,
fallback_is_src2,
},
),
);
}
rotate_dest_logic_op
}
#[hdl]
fn rotate_helper(
&mut self,
is_32bit: bool,
msb0_mask_begin: Expr<UInt<6>>,
msb0_mask_end: Expr<UInt<6>>,
ra: FieldRA,
rs: FieldRS,
rb: FieldRB,
rc: FieldRc,
) {
// TODO: handle SO propagation
connect(
ArrayVec::len(self.output),
1usize.cast_to_static::<Length<_>>(),
);
let dest_logic_op = self.rotate_dest_logic_op(msb0_mask_begin, msb0_mask_end, false);
connect(
self.output[0],
ShiftRotateMOp::shift_rotate(
MOpDestReg::new([gpr(ra.0)], [(MOpRegNum::POWER_ISA_CR_0_REG_NUM, rc.0)]),
[gpr(rs.0).value, gpr(rs.0).value, gpr(rb.0).value],
#[hdl]
ShiftRotateMOpImm {
shift_rotate_amount: HdlNone(),
shift_rotate_right: false,
dest_logic_op,
},
OutputIntegerMode.Full64(),
if is_32bit {
ShiftRotateMode.FunnelShift2x32Bit()
} else {
ShiftRotateMode.FunnelShift2x64Bit()
},
),
);
}
#[hdl]
fn rotate_imm_helper(
&mut self,
is_32bit: bool,
fallback_is_src2: bool,
msb0_mask_begin: Expr<UInt<6>>,
msb0_mask_end: Expr<UInt<6>>,
rotate_amount: Expr<UInt<6>>,
ra: FieldRA,
rs: FieldRS,
rc: FieldRc,
) {
// TODO: handle SO propagation
connect(
ArrayVec::len(self.output),
1usize.cast_to_static::<Length<_>>(),
);
let dest_logic_op =
self.rotate_dest_logic_op(msb0_mask_begin, msb0_mask_end, fallback_is_src2);
#[hdl]
let rotate_imm_src2 = wire();
connect(rotate_imm_src2, MOpRegNum::const_zero().value);
// if dest_logic_op is HdlNone, we don't need to read from src2
#[hdl]
if let HdlSome(dest_logic_op) = dest_logic_op {
#[hdl]
if dest_logic_op.fallback_is_src2 {
connect(rotate_imm_src2, gpr(ra.0).value);
}
}
connect(
self.output[0],
ShiftRotateMOp::shift_rotate(
MOpDestReg::new([gpr(ra.0)], [(MOpRegNum::POWER_ISA_CR_0_REG_NUM, rc.0)]),
[gpr(rs.0).value, gpr(rs.0).value, rotate_imm_src2],
#[hdl]
ShiftRotateMOpImm {
shift_rotate_amount: HdlSome(rotate_amount),
shift_rotate_right: false,
dest_logic_op,
},
OutputIntegerMode.Full64(),
if is_32bit {
ShiftRotateMode.FunnelShift2x32Bit()
} else {
ShiftRotateMode.FunnelShift2x64Bit()
},
),
);
}
/// for `rlwinm[.]/rlwnm[.]/rlwimi[.]/rldicl[.]/rldicr[.]/rldic[.]/rldcl[.]/rldcr[.]/rldimi[.]`
#[hdl]
fn decode_rotate(&mut self) {
match self.mnemonic.trim_end_matches('.') {
"rlwinm" => self.decode_scope(
|this, (ra, rs, FieldSH(sh), FieldMB(mb), FieldME(me), rc)| {
this.rotate_imm_helper(
true,
false,
(mb + 32u8).cast_to_static::<UInt<_>>(),
(me + 32u8).cast_to_static::<UInt<_>>(),
sh.cast_to_static::<UInt<_>>(),
ra,
rs,
rc,
);
},
),
"rlwnm" => self.decode_scope(|this, (ra, rs, rb, FieldMB(mb), FieldME(me), rc)| {
this.rotate_helper(
true,
(mb + 32u8).cast_to_static::<UInt<_>>(),
(me + 32u8).cast_to_static::<UInt<_>>(),
ra,
rs,
rb,
rc,
);
}),
"rlwimi" => self.decode_scope(
|this, (ra, rs, FieldSH(sh), FieldMB(mb), FieldME(me), rc)| {
this.rotate_imm_helper(
true,
true,
(mb + 32u8).cast_to_static::<UInt<_>>(),
(me + 32u8).cast_to_static::<UInt<_>>(),
sh.cast_to_static::<UInt<_>>(),
ra,
rs,
rc,
);
},
),
"rldicl" => self.decode_scope(|this, (ra, rs, FieldSh(sh), FieldMb(mb), rc)| {
this.rotate_imm_helper(
false,
false,
mb.rotate_right(1),
63_hdl_u6,
sh.rotate_right(1),
ra,
rs,
rc,
);
}),
"rldicr" => self.decode_scope(|this, (ra, rs, FieldSh(sh), FieldMe(me), rc)| {
this.rotate_imm_helper(
false,
false,
0_hdl_u6,
me.rotate_right(1),
sh.rotate_right(1),
ra,
rs,
rc,
);
}),
"rldic" => self.decode_scope(|this, (ra, rs, FieldSh(sh), FieldMb(mb), rc)| {
this.rotate_imm_helper(
false,
false,
mb.rotate_right(1),
!sh.rotate_right(1),
sh.rotate_right(1),
ra,
rs,
rc,
);
}),
"rldcl" => self.decode_scope(|this, (ra, rs, rb, FieldMb(mb), rc)| {
this.rotate_helper(false, mb.rotate_right(1), 63_hdl_u6, ra, rs, rb, rc);
}),
"rldcr" => self.decode_scope(|this, (ra, rs, rb, FieldMe(me), rc)| {
this.rotate_helper(false, 0_hdl_u6, me.rotate_right(1), ra, rs, rb, rc);
}),
"rldimi" => self.decode_scope(|this, (ra, rs, FieldSh(sh), FieldMb(mb), rc)| {
this.rotate_imm_helper(
false,
true,
mb.rotate_right(1),
!sh.rotate_right(1),
sh.rotate_right(1),
ra,
rs,
rc,
);
}),
_ => unreachable!("{:?}", self.mnemonic),
}
}
/// for `slw[.]/srw[.]/srawi[.]/sraw[.]/sld[.]/sradi[.]/srd[.]/srad[.]`
#[hdl]
fn decode_shift(&mut self) {
let (is_32bit, is_signed, is_right_shift, is_immediate) =
match self.mnemonic.trim_end_matches('.') {
"slw" => (true, false, false, false),
"srw" => (true, false, true, false),
"srawi" => (true, true, true, true),
"sraw" => (true, true, true, false),
"sld" => (false, false, false, false),
"sradi" => (false, true, true, true),
"srd" => (false, false, true, false),
"srad" => (false, true, true, false),
_ => unreachable!("{:?}", self.mnemonic),
};
if is_immediate {
assert!(is_signed);
assert!(is_right_shift);
assert_eq!(self.arguments, Some("RA,RS,SH"));
if is_32bit {
self.decode_scope(
|this, (FieldRA(ra), FieldRS(rs), FieldSH(sh), FieldRc(rc))| {
// TODO: handle SO propagation
connect(
ArrayVec::len(this.output),
1usize.cast_to_static::<Length<_>>(),
);
connect(
this.output[0],
ShiftRotateMOp::shift_rotate(
MOpDestReg::new(
[gpr(ra), MOpRegNum::power_isa_xer_ca_ca32_reg()],
[(MOpRegNum::POWER_ISA_CR_0_REG_NUM, rc)],
),
[
gpr(rs).value,
MOpRegNum::const_zero().value,
MOpRegNum::const_zero().value,
],
#[hdl]
ShiftRotateMOpImm {
shift_rotate_amount: HdlSome(sh.cast_to_static::<UInt<_>>()),
shift_rotate_right: true,
dest_logic_op: HdlNone(),
},
OutputIntegerMode.Full64(),
ShiftRotateMode.SignExt32To64BitThenShift(),
),
);
},
);
} else {
self.decode_scope(
|this, (FieldRA(ra), FieldRS(rs), FieldSh(sh), FieldRc(rc))| {
// TODO: handle SO propagation
connect(
ArrayVec::len(this.output),
1usize.cast_to_static::<Length<_>>(),
);
connect(
this.output[0],
ShiftRotateMOp::shift_rotate(
MOpDestReg::new(
[gpr(ra), MOpRegNum::power_isa_xer_ca_ca32_reg()],
[(MOpRegNum::POWER_ISA_CR_0_REG_NUM, rc)],
),
[
gpr(rs).value,
MOpRegNum::const_zero().value,
MOpRegNum::const_zero().value,
],
#[hdl]
ShiftRotateMOpImm {
shift_rotate_amount: HdlSome(sh.rotate_right(1)),
shift_rotate_right: true,
dest_logic_op: HdlNone(),
},
OutputIntegerMode.Full64(),
ShiftRotateMode.ShiftSigned64(),
),
);
},
);
}
} else {
assert_eq!(self.arguments, Some("RA,RS,RB"));
self.decode_scope(
|this, (FieldRA(ra), FieldRS(rs), FieldRB(rb), FieldRc(rc))| {
// TODO: handle SO propagation
connect(
ArrayVec::len(this.output),
1usize.cast_to_static::<Length<_>>(),
);
connect(
this.output[0],
ShiftRotateMOp::shift_rotate(
MOpDestReg::new(
[
gpr(ra),
if is_signed && is_right_shift {
MOpRegNum::power_isa_xer_ca_ca32_reg()
} else {
MOpRegNum::const_zero()
},
],
[(MOpRegNum::POWER_ISA_CR_0_REG_NUM, rc)],
),
if !is_signed && is_right_shift {
[MOpRegNum::const_zero().value, gpr(rs).value, gpr(rb).value]
} else {
[gpr(rs).value, MOpRegNum::const_zero().value, gpr(rb).value]
},
#[hdl]
ShiftRotateMOpImm {
shift_rotate_amount: HdlNone(),
shift_rotate_right: is_right_shift,
dest_logic_op: HdlNone(),
},
OutputIntegerMode.Full64(),
match (is_32bit, is_signed, is_right_shift) {
(false, _, false) => ShiftRotateMode.FunnelShift2x64Bit(),
(false, false, true) => ShiftRotateMode.FunnelShift2x64Bit(),
(false, true, true) => ShiftRotateMode.ShiftSigned64(),
(true, _, false) => ShiftRotateMode.FunnelShift2x32Bit(),
(true, false, true) => ShiftRotateMode.FunnelShift2x32Bit(),
(true, true, true) => ShiftRotateMode.SignExt32To64BitThenShift(),
},
),
);
},
);
}
}
/// for `extswsli[.]`
#[hdl]
fn decode_extswsli(&mut self) {
self.decode_scope(
|this, (FieldRA(ra), FieldRS(rs), FieldSh(sh), FieldRc(rc))| {
connect(
ArrayVec::len(this.output),
1usize.cast_to_static::<Length<_>>(),
);
connect(
this.output[0],
ShiftRotateMOp::shift_rotate(
MOpDestReg::new([gpr(ra)], [(MOpRegNum::POWER_ISA_CR_0_REG_NUM, rc)]),
[
gpr(rs).value,
MOpRegNum::const_zero().value,
MOpRegNum::const_zero().value,
],
#[hdl]
ShiftRotateMOpImm {
shift_rotate_amount: HdlSome(sh.rotate_right(1)),
shift_rotate_right: false,
dest_logic_op: HdlNone(),
},
OutputIntegerMode.Full64(),
ShiftRotateMode.SignExt32To64BitThenShift(),
),
);
},
);
}
fn read_write_spr(
&mut self,
spr: PowerIsaSprEnum,
reg: Expr<FieldGpr>,
is_write_to_spr: bool,
) -> Result<(), ()> {
let normal_move = |spr: Expr<MOpRegNum>| {
connect(
ArrayVec::len(self.output),
1usize.cast_to_static::<Length<_>>(),
);
if is_write_to_spr {
connect(
self.output[0],
MoveRegMOp::move_reg(
MOpDestReg::new([spr], []),
[gpr(reg).value],
0.cast_to_static::<SInt<_>>(),
),
);
} else {
connect(
self.output[0],
MoveRegMOp::move_reg(
MOpDestReg::new([gpr(reg)], []),
[spr.value],
0.cast_to_static::<SInt<_>>(),
),
);
}
Ok(())
};
let read_special = |imm: Expr<ReadSpecialMOpImm>| {
connect(
ArrayVec::len(self.output),
1usize.cast_to_static::<Length<_>>(),
);
connect(
self.output[0],
ReadSpecialMOp::read_special(
MOpDestReg::new([gpr(reg)], []),
[MOpRegNum::const_zero().value; 0],
imm,
),
);
Ok(())
};
match spr {
// PowerIsaSprEnum::Xer => todo!(),
PowerIsaSprEnum::Lr => normal_move(MOpRegNum::power_isa_lr_reg()),
PowerIsaSprEnum::Ctr => normal_move(MOpRegNum::power_isa_ctr_reg()),
PowerIsaSprEnum::Tar => normal_move(MOpRegNum::power_isa_tar_reg()),
PowerIsaSprEnum::Tb if !is_write_to_spr => {
read_special(ReadSpecialMOpImm.PowerIsaTimeBase())
}
PowerIsaSprEnum::Tbu if !is_write_to_spr => {
read_special(ReadSpecialMOpImm.PowerIsaTimeBaseU())
}
_ => {
Err(()) // allow emulation
}
}
}
/// for `mtspr/mfspr/mftb`
fn decode_mtspr_mfspr_mftb(&mut self) {
#[hdl]
fn do_read_write_spr(
this: &mut DecodeState<'_>,
spr_field: Expr<UInt<10>>,
reg: Expr<FieldGpr>,
is_write_to_spr: bool,
) {
#[hdl]
let spr_num = wire();
connect(spr_num, spr_field.rotate_left(5));
connect(this.is_illegal, true); // trap and/or allow emulation
for &spr in PowerIsaSprEnum::VARIANTS {
let num = spr.to_expr().num;
#[hdl]
if spr_num.cmp_eq(num) {
match this.read_write_spr(spr, reg, is_write_to_spr) {
Ok(()) => {
connect(this.is_illegal, false);
}
Err(()) => {
// self.is_illegal is already set to true
}
}
}
}
}
match self.mnemonic {
"mtspr" => self.decode_scope(|this, (FieldSpr(spr), FieldRS(rs))| {
do_read_write_spr(this, spr, rs, true)
}),
"mfspr" => self.decode_scope(|this, (FieldRT(rt), FieldSpr(spr))| {
do_read_write_spr(this, spr, rt, false)
}),
"mftb" => self.decode_scope(|this, (FieldRT(rt), FieldTbr(tbr))| {
do_read_write_spr(this, tbr, rt, false)
}),
_ => unreachable!(),
}
}
/// for `mcrxrx`
#[hdl]
fn decode_mcrxrx(&mut self) {
@ -1902,23 +2474,25 @@ impl DecodeState {
}
}
type DecodeFn = fn(&mut DecodeState);
type DecodeFn = fn(&mut DecodeState<'_>);
const DECODE_FNS: &[(&[&str], DecodeFn)] = &[
(&["b", "ba", "bl", "bla"], DecodeState::decode_b_ba_bl_bla),
(&["b", "ba", "bl", "bla"], |state| {
DecodeState::decode_b_ba_bl_bla(state)
}),
(
&[
"bc", "bca", "bcl", "bcla", "bclr", "bclrl", "bcctr", "bcctrl", "bctar", "bctarl",
],
DecodeState::decode_bc_bclr_bcctr_bctar,
|state| DecodeState::decode_bc_bclr_bcctr_bctar(state),
),
(
&[
"crand", "crnand", "cror", "crxor", "crnor", "creqv", "crandc", "crorc",
],
DecodeState::decode_crand_crnand_cror_crxor_crnor_creqv_crandc_crorc,
|state| DecodeState::decode_crand_crnand_cror_crxor_crnor_creqv_crandc_crorc(state),
),
(&["mcrf"], DecodeState::decode_mcrf),
(&["mcrf"], |state| DecodeState::decode_mcrf(state)),
(&["sc", "scv"], |_state| {
// TODO
}),
@ -1928,14 +2502,14 @@ const DECODE_FNS: &[(&[&str], DecodeFn)] = &[
"plha", "lhax", "lhau", "lhaux", "lwz", "plwz", "lwzx", "lwzu", "lwzux", "lwa", "plwa",
"lwax", "lwaux", "ld", "pld", "ldx", "ldu", "ldux",
],
DecodeState::decode_load_8_16_32_64_bit,
|state| DecodeState::decode_load_8_16_32_64_bit(state),
),
(
&[
"stb", "pstb", "stbx", "stbu", "stbux", "sth", "psth", "sthx", "sthu", "sthux", "stw",
"pstw", "stwx", "stwu", "stwux", "std", "pstd", "stdx", "stdu", "stdux",
],
DecodeState::decode_store_8_16_32_64_bit,
|state| DecodeState::decode_store_8_16_32_64_bit(state),
),
(&["lq", "plq", "stq", "pstq"], |_state| {
// TODO
@ -1952,43 +2526,46 @@ const DECODE_FNS: &[(&[&str], DecodeFn)] = &[
(&["lswi", "lswx", "stswi", "stswx"], |_state| {
// load/store string are intentionally not implemented
}),
(&["addi", "paddi"], DecodeState::decode_addi_paddi),
(&["addis"], DecodeState::decode_addis),
(&["addpcis"], DecodeState::decode_addpcis),
(&["add", "add.", "addo", "addo."], DecodeState::decode_add),
(&["addic", "addic."], DecodeState::decode_addic),
(
&["subf", "subf.", "subfo", "subfo."],
DecodeState::decode_subf_subfc,
),
(&["subfic"], DecodeState::decode_subfic),
(
&["addc", "addc.", "addco", "addco."],
DecodeState::decode_addc,
),
(
&["subfc", "subfc.", "subfco", "subfco."],
DecodeState::decode_subf_subfc,
),
(
&["adde", "adde.", "addeo", "addeo."],
DecodeState::decode_adde,
),
(
&["subfe", "subfe.", "subfeo", "subfeo."],
DecodeState::decode_subfe,
),
(&["addi", "paddi"], |state| {
DecodeState::decode_addi_paddi(state)
}),
(&["addis"], |state| DecodeState::decode_addis(state)),
(&["addpcis"], |state| DecodeState::decode_addpcis(state)),
(&["add", "add.", "addo", "addo."], |state| {
DecodeState::decode_add(state)
}),
(&["addic", "addic."], |state| {
DecodeState::decode_addic(state)
}),
(&["subf", "subf.", "subfo", "subfo."], |state| {
DecodeState::decode_subf_subfc(state)
}),
(&["subfic"], |state| DecodeState::decode_subfic(state)),
(&["addc", "addc.", "addco", "addco."], |state| {
DecodeState::decode_addc(state)
}),
(&["subfc", "subfc.", "subfco", "subfco."], |state| {
DecodeState::decode_subf_subfc(state)
}),
(&["adde", "adde.", "addeo", "addeo."], |state| {
DecodeState::decode_adde(state)
}),
(&["subfe", "subfe.", "subfeo", "subfeo."], |state| {
DecodeState::decode_subfe(state)
}),
(
&[
"addme", "addme.", "addmeo", "addmeo.", "addze", "addze.", "addzeo", "addzeo.",
"subfme", "subfme.", "subfmeo", "subfmeo.", "subfze", "subfze.", "subfzeo", "subfzeo.",
],
DecodeState::decode_addme_subfme_addze_subfze,
|state| DecodeState::decode_addme_subfme_addze_subfze(state),
),
(&["addex"], |_state| {
// TODO
}),
(&["neg", "neg.", "nego", "nego."], DecodeState::decode_neg),
(&["neg", "neg.", "nego", "nego."], |state| {
DecodeState::decode_neg(state)
}),
(
&[
"mulli", "mullw", "mullw.", "mullwo", "mullwo.", "mulhw", "mulhw.", "mulhwu", "mulhwu.",
@ -2029,12 +2606,12 @@ const DECODE_FNS: &[(&[&str], DecodeFn)] = &[
// TODO
},
),
(&["cmpi"], DecodeState::decode_cmpi),
(&["cmp"], DecodeState::decode_cmp),
(&["cmpli"], DecodeState::decode_cmpli),
(&["cmpl"], DecodeState::decode_cmpl),
(&["cmprb"], DecodeState::decode_cmprb),
(&["cmpeqb"], DecodeState::decode_cmpeqb),
(&["cmpi"], |state| DecodeState::decode_cmpi(state)),
(&["cmp"], |state| DecodeState::decode_cmp(state)),
(&["cmpli"], |state| DecodeState::decode_cmpli(state)),
(&["cmpl"], |state| DecodeState::decode_cmpl(state)),
(&["cmprb"], |state| DecodeState::decode_cmprb(state)),
(&["cmpeqb"], |state| DecodeState::decode_cmpeqb(state)),
(&["twi", "tw", "tdi", "td"], |_state| {
// TODO
}),
@ -2043,18 +2620,18 @@ const DECODE_FNS: &[(&[&str], DecodeFn)] = &[
}),
(
&["andi.", "andis.", "ori", "oris", "xori", "xoris"],
DecodeState::decode_andis_oris_xoris,
|state| DecodeState::decode_andis_oris_xoris(state),
),
(
&[
"and", "and.", "xor", "xor.", "nand", "nand.", "or", "or.", "orc", "orc.", "nor",
"nor.", "eqv", "eqv.", "andc", "andc.",
],
DecodeState::decode_and_xor_nand_or_orc_nor_eqv_andc,
|state| DecodeState::decode_and_xor_nand_or_orc_nor_eqv_andc(state),
),
(
&["extsb", "extsb.", "extsh", "extsh.", "extsw", "extsw."],
DecodeState::decode_extsb_extsh_extsw,
|state| DecodeState::decode_extsb_extsh_extsw(state),
),
(&["cmpb"], |_state| {
// TODO
@ -2075,21 +2652,17 @@ const DECODE_FNS: &[(&[&str], DecodeFn)] = &[
"rldicr", "rldicr.", "rldic", "rldic.", "rldcl", "rldcl.", "rldcr", "rldcr.", "rldimi",
"rldimi.",
],
|_state| {
// TODO
},
|state| DecodeState::decode_rotate(state),
),
(
&[
"slw", "slw.", "srw", "srw.", "srawi", "srawi.", "sraw", "sraw.", "sld", "sld.",
"sradi", "sradi.", "srd", "srd.", "srad", "srad.",
],
|_state| {
// TODO
},
|state| DecodeState::decode_shift(state),
),
(&["extswsli", "extswsli."], |_state| {
// TODO
(&["extswsli", "extswsli."], |state| {
DecodeState::decode_extswsli(state)
}),
(&["cdtbcd", "cbcdtd", "addg6s"], |_state| {
// TODO
@ -2108,13 +2681,13 @@ const DECODE_FNS: &[(&[&str], DecodeFn)] = &[
// TODO(FP) -- mostly intentionally not implemented
},
),
(
&["mtspr", "mfspr", "mftb", "mtmsr", "mtmsrd", "mfmsr"],
|_state| {
// TODO
},
),
(&["mcrxrx"], DecodeState::decode_mcrxrx),
(&["mtspr", "mfspr", "mftb"], |state| {
DecodeState::decode_mtspr_mfspr_mftb(state)
}),
(&["mtmsr", "mtmsrd", "mfmsr"], |_state| {
// TODO
}),
(&["mcrxrx"], |state| DecodeState::decode_mcrxrx(state)),
(
&[
"mtocrf", "mtcrf", "mfocrf", "mfcr", "setb", "setbc", "setbcr", "setnbc", "setnbcr",
@ -2123,7 +2696,7 @@ const DECODE_FNS: &[(&[&str], DecodeFn)] = &[
// TODO
},
),
(&["pnop"], DecodeState::decode_pnop),
(&["pnop"], |state| DecodeState::decode_pnop(state)),
(FP_MNEMONICS, |_state| {
// TODO(FP)
}),
@ -2217,6 +2790,7 @@ pub fn decode_one_insn() {
assert!(!duplicate, "duplicate mnemonic in DECODE_FNS: {mnemonic:?}");
}
}
let mut field_wires = Default::default();
for insn in Instructions::get().instructions() {
for header in insn.header() {
for mnemonic_line in header.mnemonics().lines() {
@ -2260,6 +2834,7 @@ pub fn decode_one_insn() {
first_input,
second_input,
second_input_used,
field_wires: &mut field_wires,
});
}
}

81
crates/cpu/src/fetch.rs Normal file
View file

@ -0,0 +1,81 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::config::{
CpuConfig, CpuConfigCacheLineSizeInBytes, CpuConfigFetchWidthInBytes,
CpuConfigL1ICacheLineCount, CpuConfigMaxFetchesInFlight, PhantomConstCpuConfig,
};
use fayalite::{
int::UIntInRangeInclusiveType, memory::ReadWriteStruct, prelude::*,
util::ready_valid::ReadyValid,
};
#[hdl]
pub enum MemoryOperationKind {
Read,
Write,
}
#[hdl(no_static)]
pub struct MemoryOperationStart<C: PhantomConstGet<CpuConfig> + PhantomConstCpuConfig> {
pub kind: MemoryOperationKind,
pub addr: UInt<64>,
pub write_data: ArrayType<UInt<8>, CpuConfigFetchWidthInBytes<C>>,
pub config: C,
}
#[hdl]
pub enum MemoryOperationErrorKind {
Generic,
}
#[hdl]
pub enum MemoryOperationFinishKind {
Success(MemoryOperationKind),
Error(MemoryOperationErrorKind),
}
#[hdl(no_static)]
pub struct MemoryOperationFinish<C: PhantomConstGet<CpuConfig> + PhantomConstCpuConfig> {
pub kind: MemoryOperationFinishKind,
pub read_data: ArrayType<UInt<8>, CpuConfigFetchWidthInBytes<C>>,
pub config: C,
}
#[hdl(no_static)]
pub struct MemoryInterface<C: PhantomConstGet<CpuConfig> + PhantomConstCpuConfig> {
pub start: ReadyValid<MemoryOperationStart<C>>,
/// when both start and cancel are triggered in the same clock cycle, that means to cancel and then start a new memory operation
pub cancel: ReadyValid<UIntInRangeInclusiveType<ConstUsize<1>, CpuConfigMaxFetchesInFlight<C>>>,
#[hdl(flip)]
pub finish: ReadyValid<MemoryOperationFinish<C>>,
pub config: C,
}
#[hdl]
type CacheLine<C: PhantomConstGet<CpuConfig> + PhantomConstCpuConfig> =
ArrayType<UInt<8>, CpuConfigCacheLineSizeInBytes<C>>;
#[hdl_module]
pub fn l1_i_cache(config: PhantomConst<CpuConfig>) {
#[hdl]
let cd: ClockDomain = m.input();
#[hdl]
let memory_interface: MemoryInterface<PhantomConst<CpuConfig>> =
m.output(MemoryInterface[config]);
#[hdl]
let mut i_cache =
memory_array(ArrayType[CacheLine[config]][CpuConfigL1ICacheLineCount[config]]);
#[hdl]
let ReadWriteStruct::<_, _> {
addr: port_addr,
en: port_en,
clk: port_clk,
rdata: port_rdata,
wmode: port_wmode,
wdata: port_wdata,
wmask: port_wmask,
} = i_cache.new_rw_port();
connect(port_clk, cd.clk);
todo!()
}

File diff suppressed because it is too large Load diff

View file

@ -221,3 +221,238 @@ impl MOpRegNum {
}
}
}
#[hdl(cmp_eq)]
pub struct PowerIsaSpr {
pub num: UInt<10>,
}
macro_rules! make_spr_enum {
(
$(#[$enum_meta:meta])*
$enum_vis:vis enum $PowerIsaSprEnum:ident {
$($enum_body:tt)*
}
) => {
$(#[$enum_meta])*
$enum_vis enum $PowerIsaSprEnum {
$($enum_body)*
Unknown(u16),
}
make_spr_enum! {
@impl
$enum_vis enum $PowerIsaSprEnum {
$($enum_body)*
}
}
};
(
@impl
$enum_vis:vis enum $PowerIsaSprEnum:ident {
$(
$(#[$variant_meta:meta])*
$Variant:ident = $value:literal,
)+
}
) => {
impl $PowerIsaSprEnum {
pub const VARIANTS: &[Self; 1 << 10] = &{
let mut retval = [Self::Unknown(0); 1 << 10];
let mut i = 0;
while i < retval.len() {
retval[i] = Self::Unknown(i as u16);
i += 1;
}
let mut last_value = None;
#[track_caller]
const fn add_variant(
values: &mut [$PowerIsaSprEnum; 1 << 10],
last_value: &mut Option<u16>,
variant: $PowerIsaSprEnum,
value: u16,
) {
assert!(value < 1 << 10, "variant value out of range");
if let Some(last_value) = *last_value {
assert!(last_value < value, "variants must be in ascending order with no duplicates");
}
*last_value = Some(value);
values[value as usize] = variant;
}
$(add_variant(&mut retval, &mut last_value, Self::$Variant, $value);)+
retval
};
pub const fn value(self) -> u16 {
match self {
$(Self::$Variant => $value,)+
Self::Unknown(v) => v,
}
}
}
};
}
make_spr_enum! {
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[repr(u16)]
pub enum PowerIsaSprEnum {
Xer = 1,
UserDscr = 3,
Lr = 8,
Ctr = 9,
UserAmr = 13,
Dscr = 17,
Dsisr = 18,
Dar = 19,
Dec = 22,
Srr0 = 26,
Srr1 = 27,
Cfar = 28,
Amr = 29,
Pidr = 48,
Iamr = 61,
ReadCtrl = 136,
WriteCtrl = 152,
Fscr = 153,
Uamor = 157,
Pspb = 159,
Dpdes = 176,
Dawr0 = 180,
Dawr1 = 181,
Rpr = 186,
Ciabr = 187,
Dawrx0 = 188,
Dawrx1 = 189,
Hfscr = 190,
Vrsave = 256,
UserSprg3 = 259,
Tb = 268,
Tbu = 269,
Sprg0 = 272,
Sprg1 = 273,
Sprg2 = 274,
Sprg3 = 275,
Tbl = 284,
WriteTbu = 285,
Tbu40 = 286,
Pvr = 287,
Hsprg0 = 304,
Hsprg1 = 305,
Hdsisr = 306,
Hdar = 307,
Spurr = 308,
Purr = 309,
Hdec = 310,
Hrmor = 313,
Hsrr0 = 314,
Hsrr1 = 315,
Lpcr = 318,
Lpidr = 319,
Hmer = 336,
Hmeer = 337,
Pcr = 338,
Heir = 339,
Amor = 349,
Tir = 446,
UserHdexcr = 455,
Ptcr = 464,
Hashkeyr = 468,
Hashpkeyr = 469,
Hdexcr = 471,
Usprg0 = 496,
Usprg1 = 497,
Urmor = 505,
Usrr0 = 506,
Usrr1 = 507,
Smfctrl = 511,
UserSier2 = 736,
UserSier3 = 737,
UserMmcr3 = 738,
Sier2 = 752,
Sier3 = 753,
Mmcr3 = 754,
UserSier = 768,
Mmcr2 = 769,
Mmcra = 770,
Pmc1 = 771,
Pmc2 = 772,
Pmc3 = 773,
Pmc4 = 774,
Pmc5 = 775,
Pmc6 = 776,
Mmcr0 = 779,
Siar = 780,
Sdar = 781,
Mmcr1 = 782,
Sier = 784,
PrivMmcr2 = 785,
PrivMmcra = 786,
PrivPmc1 = 787,
PrivPmc2 = 788,
PrivPmc3 = 789,
PrivPmc4 = 790,
PrivPmc5 = 791,
PrivPmc6 = 792,
PrivMmcr0 = 795,
PrivSiar = 796,
PrivSdar = 797,
PrivMmcr1 = 798,
Bescrs15 = 800,
Bescrsu16 = 801,
Bescrr15 = 802,
Bescrru16 = 803,
Ebbhr = 804,
Ebbrr = 805,
Bescr = 806,
Reserved808 = 808,
Reserved809 = 809,
Reserved810 = 810,
Reserved811 = 811,
UserDexcr = 812,
Tar = 815,
Asdr = 816,
Psscr = 823,
Dexcr = 828,
Ic = 848,
Vtb = 849,
HyperPsscr = 855,
Ppr = 896,
Ppr32 = 898,
Pir = 1023,
}
}
impl ValueType for PowerIsaSprEnum {
type Type = PowerIsaSpr;
type ValueCategory = fayalite::expr::value_category::ValueCategoryValue;
fn ty(&self) -> Self::Type {
PowerIsaSpr
}
}
impl ToExpr for PowerIsaSprEnum {
#[hdl]
fn to_expr(&self) -> Expr<Self::Type> {
#[hdl]
PowerIsaSpr {
num: self.value().cast_to_static::<UInt<_>>(),
}
}
}
impl ToSimValueWithType<PowerIsaSpr> for PowerIsaSprEnum {
fn to_sim_value_with_type(&self, _ty: PowerIsaSpr) -> SimValue<PowerIsaSpr> {
self.to_sim_value()
}
}
impl ToSimValue for PowerIsaSprEnum {
#[hdl]
fn to_sim_value(&self) -> SimValue<Self::Type> {
#[hdl(sim)]
PowerIsaSpr {
num: self.value().cast_to_static::<UInt<_>>(),
}
}
}

View file

@ -2,6 +2,7 @@
// See Notices.txt for copyright information
pub mod config;
pub mod decoder;
pub mod fetch;
pub mod instruction;
pub mod next_pc;
pub mod powerisa_instructions_xml;

View file

@ -4,9 +4,9 @@
use crate::{
config::CpuConfig,
instruction::{
AddSubMOp, AluBranchMOp, AluCommonMOp, BranchMOp, COMMON_MOP_SRC_LEN, CommonMOp,
CompareMOp, LogicalFlagsMOp, LogicalMOp, MOpTrait, OutputIntegerMode, RenamedMOp,
ShiftRotateMOp, UnitOutRegNum,
AddSubMOp, AluBranchMOp, AluCommonMOp, BranchMOp, COMMON_MOP_SRC_LEN, CommonMOpDefaultImm,
CompareMOp, LogicalFlagsMOp, LogicalMOp, MOpTrait, OutputIntegerMode, ReadSpecialMOp,
RenamedMOp, ShiftRotateMOp, UnitOutRegNum,
},
register::{
FlagsMode, PRegFlagsPowerISA, PRegFlagsPowerISAView, PRegFlagsViewTrait, PRegFlagsX86,
@ -42,11 +42,11 @@ fn add_sub<SrcCount: KnownSize>(
add_pc,
} = mop;
#[hdl]
let AluCommonMOp::<_, _, _> {
let AluCommonMOp::<_, _, _, _> {
common,
output_integer_mode,
} = alu_common;
let imm: Expr<UInt<64>> = CommonMOp::imm(common).cast_to_static();
let imm = CommonMOpDefaultImm::as_sint_dyn(common.imm).cast_to_static::<UInt<64>>();
#[hdl]
let carry_in_before_inversion = wire();
connect(carry_in_before_inversion, false);
@ -328,6 +328,21 @@ fn branch<SrcCount: KnownSize>(
}
}
#[hdl]
fn read_special(
mop: Expr<ReadSpecialMOp<UnitOutRegNum<DynSize>, DynSize>>,
pc: Expr<UInt<64>>,
flags_mode: Expr<FlagsMode>,
src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
) -> Expr<UnitResultCompleted<()>> {
// TODO: finish
#[hdl]
UnitResultCompleted::<_> {
value: PRegValue::zeroed(),
extra_out: (),
}
}
#[hdl_module]
pub fn alu_branch(config: &CpuConfig, unit_index: usize) {
#[hdl]
@ -541,6 +556,24 @@ pub fn alu_branch(config: &CpuConfig, unit_index: usize) {
},
),
),
AluBranchMOp::<_, _>::ReadSpecial(mop) => connect(
unit_base.execute_end,
HdlSome(
#[hdl]
ExecuteEnd::<_, _> {
unit_output: #[hdl]
UnitOutput::<_, _> {
which: MOpTrait::dest_reg(mop),
result: UnitResult[()].Completed(read_special(
mop,
pc,
global_state.flags_mode,
src_values,
)),
},
},
),
),
}
}
}

View file

@ -38,6 +38,54 @@ pub trait Rotate<Amount> {
fn rotate_right(&self, amount: Amount) -> Self::Output;
}
impl<VSz: Size> Rotate<usize> for Expr<UIntType<VSz>> {
type Output = Self;
/// like [`usize::rotate_left`]
fn rotate_left(&self, amount: usize) -> Self::Output {
if self.ty().width() == 0 {
return *self;
}
let amount = amount % self.ty().width();
let l = *self << amount;
let r = *self >> (self.ty().width() - amount);
(l | r).cast_to(self.ty())
}
/// like [`usize::rotate_right`]
fn rotate_right(&self, amount: usize) -> Self::Output {
if self.ty().width() == 0 {
return *self;
}
let amount = amount % self.ty().width();
let l = *self << (self.ty().width() - amount);
let r = *self >> amount;
(l | r).cast_to(self.ty())
}
}
impl<VSz: Size> Rotate<usize> for SimValue<UIntType<VSz>> {
type Output = Self;
/// like [`usize::rotate_left`]
fn rotate_left(&self, amount: usize) -> Self::Output {
if self.ty().width() == 0 {
return self.clone();
}
let amount = amount % self.ty().width();
let l = self << amount;
let r = self >> (self.ty().width() - amount);
(l | r).cast_to(self.ty())
}
/// like [`usize::rotate_right`]
fn rotate_right(&self, amount: usize) -> Self::Output {
if self.ty().width() == 0 {
return self.clone();
}
let amount = amount % self.ty().width();
let l = self << (self.ty().width() - amount);
let r = self >> amount;
(l | r).cast_to(self.ty())
}
}
impl<VSz: Size, ASz: Size> Rotate<Expr<UIntType<ASz>>> for Expr<UIntType<VSz>> {
type Output = Self;
/// like [`usize::rotate_left`]
@ -176,3 +224,59 @@ impl<T: Type, N: Size, AmountSize: Size> Rotate<SimValue<UIntType<AmountSize>>>
retval
}
}
impl<T: Type, N: Size> Rotate<usize> for Expr<ArrayType<T, N>> {
type Output = Self;
/// like [`<[T]>::rotate_left`](slice::rotate_left)
fn rotate_left(&self, amount: usize) -> Self::Output {
if self.ty().len() == 0 {
return self.clone();
}
let amount = amount % self.ty().len();
let mut retval = Vec::from_iter(*self);
retval.rotate_left(amount);
ArrayLiteral::new(
self.ty().element(),
retval.into_iter().map(Expr::canonical).collect(),
)
.to_expr()
}
/// like [`<[T]>::rotate_right`](slice::rotate_right)
fn rotate_right(&self, amount: usize) -> Self::Output {
if self.ty().len() == 0 {
return self.clone();
}
let amount = amount % self.ty().len();
let mut retval = Vec::from_iter(*self);
retval.rotate_right(amount);
ArrayLiteral::new(
self.ty().element(),
retval.into_iter().map(Expr::canonical).collect(),
)
.to_expr()
}
}
impl<T: Type, N: Size> Rotate<usize> for SimValue<ArrayType<T, N>> {
type Output = Self;
/// like [`<[T]>::rotate_left`](slice::rotate_left)
fn rotate_left(&self, amount: usize) -> Self::Output {
if self.ty().len() == 0 {
return self.clone();
}
let amount = amount % self.ty().len();
let mut retval = self.clone();
AsMut::<[SimValue<T>]>::as_mut(&mut SimValue::value_mut(&mut retval)).rotate_left(amount);
retval
}
/// like [`<[T]>::rotate_right`](slice::rotate_right)
fn rotate_right(&self, amount: usize) -> Self::Output {
if self.ty().len() == 0 {
return self.clone();
}
let amount = amount % self.ty().len();
let mut retval = self.clone();
AsMut::<[SimValue<T>]>::as_mut(&mut SimValue::value_mut(&mut retval)).rotate_right(amount);
retval
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -54,6 +54,17 @@ fn test_test_cases_assembly() -> std::io::Result<()> {
let mut lines = stdout.lines();
let text_line = lines.next();
assert_eq!(text_line, Some("\t.text"));
let mut any_error = false;
macro_rules! assert_eq_cont {
($l:expr, $r:expr, $($msg:tt)+) => {
match (&$l, &$r) {
(l, r) => if l != r {
eprintln!("assertion failed: {}\nl={l:#?}\nr={r:#?}", format_args!($($msg)+));
any_error = true;
}
}
};
}
for test_case @ TestCase {
mnemonic: _,
first_input,
@ -95,14 +106,18 @@ fn test_test_cases_assembly() -> std::io::Result<()> {
} else {
format!(" encoding: [0x{b0:02x},0x{b1:02x},0x{b2:02x},0x{b3:02x}]")
};
assert_eq!(
comment, expected_comment,
assert_eq_cont!(
comment,
expected_comment,
"test_case={test_case:?}\nline:\n{line}"
);
}
for line in lines {
assert!(line.trim().is_empty(), "bad trailing output line: {line:?}");
}
if any_error {
panic!();
}
Ok(())
}

View file

@ -11,6 +11,7 @@ mod fixed_point_arithmetic;
mod fixed_point_compare;
mod fixed_point_load;
mod fixed_point_logical;
mod fixed_point_rotate_and_shift;
mod fixed_point_store;
mod move_to_from_system_register;
mod prefixed_no_operation;
@ -143,9 +144,15 @@ pub fn test_cases() -> Vec<TestCase> {
fixed_point_arithmetic::test_cases_book_i_3_3_9_fixed_point_arithmetic(&mut retval);
fixed_point_compare::test_cases_book_i_3_3_10_fixed_point_compare(&mut retval);
fixed_point_logical::test_cases_book_i_3_3_13_fixed_point_logical(&mut retval);
fixed_point_rotate_and_shift::test_cases_book_i_3_3_14_fixed_point_rotate_and_shift(
&mut retval,
);
move_to_from_system_register::test_cases_book_i_3_3_19_move_to_from_system_register(
&mut retval,
);
prefixed_no_operation::test_cases_book_i_3_3_20_prefixed_no_operation(&mut retval);
move_to_from_system_register::test_cases_book_iii_5_4_4_move_to_from_system_register(
&mut retval,
);
retval
}

View file

@ -2,11 +2,15 @@
// See Notices.txt for copyright information
use crate::test_cases::{TestCase, insn_single};
use cpu::instruction::{LogicalFlagsMOp, LogicalFlagsMOpImm, Lut4, MOpDestReg, MOpRegNum};
use cpu::instruction::{
LogicalFlagsMOp, LogicalFlagsMOpImm, Lut4, MOpDestReg, MOpRegNum, MoveRegMOp, ReadSpecialMOp,
ReadSpecialMOpImm,
};
use fayalite::prelude::*;
/// covers instructions in PowerISA v3.1C Book I 3.3.19 Move To/From System Register Instructions
pub fn test_cases_book_i_3_3_19_move_to_from_system_register(retval: &mut Vec<TestCase>) {
// mfspr/mtspr are covered by test_cases_book_iii_5_4_4_move_to_from_system_register
#[hdl]
fn mcrxrx_imm() -> SimValue<LogicalFlagsMOpImm> {
#[hdl(sim)]
@ -35,3 +39,111 @@ pub fn test_cases_book_i_3_3_19_move_to_from_system_register(retval: &mut Vec<Te
),
));
}
/// covers instructions in PowerISA v3.1C Book III 5.4.4 Move To/From System Register Instructions
pub fn test_cases_book_iii_5_4_4_move_to_from_system_register(retval: &mut Vec<TestCase>) {
retval.push(insn_single(
"mflr 3",
0x7c6802a6,
None,
MoveRegMOp::move_reg(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[MOpRegNum::power_isa_lr_reg().value],
0.cast_to_static::<SInt<_>>(),
),
));
retval.push(insn_single(
"mtlr 3",
0x7c6803a6,
None,
MoveRegMOp::move_reg(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_LR_REG_NUM], &[]),
[MOpRegNum::power_isa_gpr_reg_imm(3).value],
0.cast_to_static::<SInt<_>>(),
),
));
retval.push(insn_single(
"mfctr 3",
0x7c6902a6,
None,
MoveRegMOp::move_reg(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[MOpRegNum::power_isa_ctr_reg().value],
0.cast_to_static::<SInt<_>>(),
),
));
retval.push(insn_single(
"mtctr 3",
0x7c6903a6,
None,
MoveRegMOp::move_reg(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_CTR_REG_NUM], &[]),
[MOpRegNum::power_isa_gpr_reg_imm(3).value],
0.cast_to_static::<SInt<_>>(),
),
));
retval.push(insn_single(
"mfspr 3, 815 # mftar 3",
0x7c6fcaa6,
None,
MoveRegMOp::move_reg(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[MOpRegNum::power_isa_tar_reg().value],
0.cast_to_static::<SInt<_>>(),
),
));
retval.push(insn_single(
"mtspr 815, 3 # mttar 3",
0x7c6fcba6,
None,
MoveRegMOp::move_reg(
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TAR_REG_NUM], &[]),
[MOpRegNum::power_isa_gpr_reg_imm(3).value],
0.cast_to_static::<SInt<_>>(),
),
));
// make sure we generate mfspr and not the phased-out mftb
retval.push(insn_single(
"mfspr 3, 268 # mftb 3",
0x7c6c42a6,
None,
ReadSpecialMOp::read_special(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[MOpRegNum::const_zero().value; 0],
ReadSpecialMOpImm.PowerIsaTimeBase(),
),
));
// make sure we generate mfspr and not the phased-out mftb
retval.push(insn_single(
"mfspr 3, 269 # mftbu 3",
0x7c6d42a6,
None,
ReadSpecialMOp::read_special(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[MOpRegNum::const_zero().value; 0],
ReadSpecialMOpImm.PowerIsaTimeBaseU(),
),
));
// phased-out mftb -- not actually generated by the assembler so we have to use .long
retval.push(insn_single(
".long 0x7c6c42e6 # mftb 3, 268",
0x7c6c42e6,
None,
ReadSpecialMOp::read_special(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[MOpRegNum::const_zero().value; 0],
ReadSpecialMOpImm.PowerIsaTimeBase(),
),
));
// phased-out mftb -- not actually generated by the assembler so we have to use .long
retval.push(insn_single(
".long 0x7c6d42e6 # mftb 3, 269",
0x7c6d42e6,
None,
ReadSpecialMOp::read_special(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
[MOpRegNum::const_zero().value; 0],
ReadSpecialMOpImm.PowerIsaTimeBaseU(),
),
));
}