forked from libre-chip/cpu
Compare commits
10 commits
59874b9b29
...
de81b9d8a4
| Author | SHA1 | Date | |
|---|---|---|---|
| de81b9d8a4 | |||
| c62d33048c | |||
| 596440755c | |||
| 68a4373bbd | |||
| f88346ea37 | |||
| a42b76b468 | |||
| 130c1b2892 | |||
| 167bc4b6a6 | |||
| faa8dde774 | |||
| 1db65ae753 |
17 changed files with 188979 additions and 204028 deletions
|
|
@ -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
13
Cargo.lock
generated
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
81
crates/cpu/src/fetch.rs
Normal 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
|
|
@ -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<_>>(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)),
|
||||
},
|
||||
},
|
||||
),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
129127
crates/cpu/tests/expected/reg_alloc.vcd
generated
129127
crates/cpu/tests/expected/reg_alloc.vcd
generated
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -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(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -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(),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue