Skip to content

Commit

Permalink
Generic CSR Functionality (#151)
Browse files Browse the repository at this point in the history
  • Loading branch information
kimmeljo authored Jan 30, 2025
1 parent 519f987 commit dd22bf5
Show file tree
Hide file tree
Showing 7 changed files with 2,189 additions and 1 deletion.
243 changes: 243 additions & 0 deletions doc/components/csr.md

Large diffs are not rendered by default.

198 changes: 198 additions & 0 deletions lib/src/memory/csr/csr.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
// Copyright (C) 2024-2025 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// csr.dart
// A flexible definition of CSRs.
//
// 2024 December
// Author: Josh Kimmel <[email protected]>

import 'package:rohd/rohd.dart';
import 'package:rohd_hcl/rohd_hcl.dart';

/// Logic representation of a CSR.
///
/// Semantically, a register can be created with no fields.
/// In this case, a single implicit field is created that is
/// read/write and the entire width of the register.
class Csr extends LogicStructure {
// internal config object which is private
final CsrInstanceConfig _config;

/// Configuration for the CSR.
CsrInstanceConfig get config => _config.clone();

/// A list of indices of all of the reserved fields in the CSR.
/// This is necessary because the configuration does not explicitly
/// define reserved fields, but they must be accounted for
/// in certain logic involving the CSR.
final List<int> rsvdIndices;

/// Getter for the address of the CSR.
int get addr => config.addr;

/// Getter for the reset value of the CSR.
int get resetValue => config.resetValue;

/// Getter for the access control of the CSR.
CsrAccess get access => config.access;

/// Accessor to the architectural frontdoor readability of the register.
bool get isFrontdoorReadable => config.isFrontdoorReadable;

/// Accessor to the architectural frontdoor writability of the register.
bool get isFrontdoorWritable => config.isFrontdoorWritable;

/// Accessor to the architectural backdoor readability of the register.
bool get isBackdoorReadable => config.isBackdoorReadable;

/// Accessor to the architectural backdoor writability of the register.
bool get isBackdoorWritable => config.isBackdoorWritable;

/// Getter for the field configuration of the CSR
List<CsrFieldConfig> get fields => config.fields;

/// Factory constructor for [Csr].
///
/// Because LogicStructure requires a List<Logic> upon construction,
/// the factory method assists in creating the List upfront before
/// the LogicStructure constructor is called.
factory Csr(
CsrInstanceConfig config,
) {
final fields = <Logic>[];
final rsvds = <int>[];
var currIdx = 0;
var rsvdCount = 0;

// semantically a register with no fields means that
// there is one read/write field that is the entire register
if (config.fields.isEmpty) {
fields.add(Logic(name: '${config.name}_data', width: config.width));
}
// there is at least one field explicitly defined so
// process them individually
else {
for (final field in config.fields) {
if (field.start > currIdx) {
fields.add(Logic(
name: '${config.name}_rsvd_$rsvdCount',
width: field.start - currIdx));
rsvds.add(fields.length - 1);
rsvdCount++;
}
fields.add(
Logic(name: '${config.name}_${field.name}', width: field.width));
currIdx = field.start + field.width;
}
if (currIdx < config.width) {
fields.add(Logic(
name: '${config.name}_rsvd_$rsvdCount',
width: config.width - currIdx));
rsvds.add(fields.length - 1);
}
}
return Csr._(
config: config,
rsvdIndices: rsvds,
fields: fields,
);
}

/// Explicit constructor.
///
/// This constructor is private and should not be used directly.
/// Instead, the factory constructor [Csr] should be used.
/// This facilitates proper calling of the super constructor.
Csr._({
required CsrInstanceConfig config,
required this.rsvdIndices,
required List<Logic> fields,
}) : _config = config.clone(),
super(fields, name: config.name) {
_config.validate();
}

/// Accessor to the bits of a particular field
/// within the CSR by name [name].
Logic getField(String name) =>
elements.firstWhere((element) => element.name == '${this.name}_$name');

/// Accessor to the config of a particular field
/// within the CSR by name [name].
CsrFieldConfig getFieldConfigByName(String name) =>
config.getFieldByName(name);

/// Given some arbitrary data [wd] to write to this CSR,
/// return the data that should actually be written based
/// on the access control of the CSR and its fields.
Logic getWriteData(Logic wd) {
// if the whole register is ready only, return the current value
if (access == CsrAccess.readOnly) {
return this;
}
// register can be written, but still need to look at the fields...
else {
// special case of no explicit fields defined
// in this case, we have an implicit read/write field
// so there is nothing special to do
if (fields.isEmpty) {
return wd;
}

// otherwise, we need to look at the fields
var finalWd = wd;
var currIdx = 0;
var currField = 0;
for (var i = 0; i < elements.length; i++) {
// if the given field is reserved or read only
// take the current value instead of the new value
final chk1 = rsvdIndices.contains(i);
if (chk1) {
finalWd = finalWd.withSet(currIdx, elements[i]);
currIdx += elements[i].width;
continue;
}

// if the given field is read only
// take the current value instead of the new value
final chk2 = fields[currField].access == CsrFieldAccess.readOnly ||
fields[currField].access == CsrFieldAccess.writeOnesClear;
if (chk2) {
finalWd = finalWd.withSet(currIdx, elements[i]);
currField++;
currIdx += elements[i].width;
continue;
}

if (fields[currField].access == CsrFieldAccess.readWriteLegal) {
// if the given field is write legal
// make sure the value is in fact legal
// and transform it if not
final origVal =
wd.getRange(currIdx, currIdx + fields[currField].width);
final legalCases = <Logic, Logic>{};
for (var i = 0; i < fields[currField].legalValues.length; i++) {
legalCases[Const(fields[currField].legalValues[i],
width: fields[currField].width)] = origVal;
}
final newVal = cases(
origVal,
conditionalType: ConditionalType.unique,
legalCases,
defaultValue: Const(fields[currField].transformIllegalValue(),
width: fields[currField].width));

finalWd = finalWd.withSet(currIdx, newVal);
currField++;
currIdx += elements[i].width;
} else {
// normal read/write field
currField++;
currIdx += elements[i].width;
}
}
return finalWd;
}
}
}
Loading

0 comments on commit dd22bf5

Please sign in to comment.