mirror of
https://github.com/saymrwulf/xous-core.git
synced 2026-11-08 04:56:43 +00:00
support multi-svd file designs
This patch set goes across all the toolchains and brings in the support for split SVD descriptions of SoCs. This is helpful for dealing with designs that have multiple "top" files for the same core IP set. For exmaple, you may have a SoC that you are developing that targets silicon, but you have a version that wraps this in additional peripherals available only in an FPGA emulator that are used for debugging. In this case, you can have one SVD file be the description of the SoC itself, and another SVD file describe just the "delta" introduced by the FPGA wrapper that emulates/controls the SoC.
This commit is contained in:
parent
cde56555d4
commit
069edbc255
9 changed files with 159 additions and 64 deletions
|
|
@ -5,8 +5,7 @@
|
|||
use quick_xml::events::{attributes::Attribute, Event};
|
||||
use quick_xml::reader::Reader;
|
||||
use quick_xml::name::QName;
|
||||
use std::io::{BufRead, Write};
|
||||
use std::path::Path;
|
||||
use std::io::{BufRead, BufReader, Read, Write};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ParseError {
|
||||
|
|
@ -479,9 +478,20 @@ fn parse_memory_regions<T: BufRead>(
|
|||
loop {
|
||||
match reader.read_event_into(&mut buf) {
|
||||
Ok(Event::Start(ref e)) => match e.name() {
|
||||
QName(b"memoryRegion") => description
|
||||
QName(b"memoryRegion") => {
|
||||
let mut mr = generate_memory_region(reader)?;
|
||||
// keep adding _ to the end of the name until it's unique
|
||||
loop {
|
||||
if description.memory_regions.iter().find(|&m| m.name == mr.name).is_some() {
|
||||
mr.name.push_str("_");
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
description
|
||||
.memory_regions
|
||||
.push(generate_memory_region(reader)?),
|
||||
.push(mr)
|
||||
},
|
||||
_ => panic!("unexpected tag in <memoryRegions>: {:?}", e),
|
||||
},
|
||||
Ok(Event::End(ref e)) => match e.name() {
|
||||
|
|
@ -525,6 +535,14 @@ fn generate_constants<T: BufRead>(
|
|||
_ => panic!("unexpected value in constant: {:?}", maybe_att),
|
||||
}
|
||||
}
|
||||
// keep adding _ to the end of the name until it's unique
|
||||
loop {
|
||||
if description.constants.iter().find(|&c| c.name == constant_descriptor.name).is_some() {
|
||||
constant_descriptor.name.push_str("_");
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
description.constants.push(constant_descriptor)
|
||||
}
|
||||
_ => panic!("unexpected tag in <constants>: {:?}", e),
|
||||
|
|
@ -1014,31 +1032,34 @@ mod tests {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn parse_svd<P: AsRef<Path>>(src: P) -> Result<Description, ParseError> {
|
||||
let mut buf = Vec::new();
|
||||
let mut reader = Reader::from_file(src).expect("couldn't open SVD for reading");
|
||||
pub fn parse_svd<T: Read>(sources: Vec::<T>) -> Result<Description, ParseError> {
|
||||
let mut description = Description::default();
|
||||
loop {
|
||||
match reader.read_event_into(&mut buf) {
|
||||
Ok(Event::Start(ref e)) => match e.name() {
|
||||
QName(b"peripherals") => {
|
||||
description.peripherals = generate_peripherals(&mut reader)?;
|
||||
}
|
||||
QName(b"vendorExtensions") => {
|
||||
parse_vendor_extensions(&mut reader, &mut description)?;
|
||||
}
|
||||
for src in sources {
|
||||
let mut buf = Vec::new();
|
||||
let buf_reader = BufReader::new(src);
|
||||
let mut reader = Reader::from_reader(buf_reader);
|
||||
loop {
|
||||
match reader.read_event_into(&mut buf) {
|
||||
Ok(Event::Start(ref e)) => match e.name() {
|
||||
QName(b"peripherals") => {
|
||||
description.peripherals = generate_peripherals(&mut reader)?;
|
||||
}
|
||||
QName(b"vendorExtensions") => {
|
||||
parse_vendor_extensions(&mut reader, &mut description)?;
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Ok(Event::Eof) => break,
|
||||
Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e),
|
||||
_ => (),
|
||||
},
|
||||
Ok(Event::Eof) => break,
|
||||
Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e),
|
||||
_ => (),
|
||||
}
|
||||
buf.clear();
|
||||
}
|
||||
buf.clear();
|
||||
}
|
||||
Ok(description)
|
||||
}
|
||||
|
||||
pub fn generate<T: AsRef<Path>, U: Write>(src: T, dest: &mut U) -> Result<(), ParseError> {
|
||||
pub fn generate<T: Read, U: Write>(src: Vec::<T>, dest: &mut U) -> Result<(), ParseError> {
|
||||
let description = parse_svd(src)?;
|
||||
|
||||
print_header(dest).or(Err(ParseError::WriteError))?;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,6 @@ mod tests {
|
|||
fn basic_generate() {
|
||||
DirBuilder::new().recursive(true).create("target").unwrap();
|
||||
let mut dest = File::create("target/example.rs").unwrap();
|
||||
generate("examples/soc.svd", &mut dest).unwrap();
|
||||
generate(vec![std::fs::File::open("examples/soc.svd").unwrap()], &mut dest).unwrap();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,8 +11,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
let generated_filename = std::env::args().nth(2).ok_or("Must specify destination utralib filename")?;
|
||||
|
||||
let mut dest_file = std::fs::File::create(generated_filename).expect("couldn't open dest file");
|
||||
let src_file = std::fs::File::open(svd_filename).expect("couldn't open source file");
|
||||
|
||||
svd2utra::generate(svd_filename, &mut dest_file)?;
|
||||
svd2utra::generate(vec![src_file], &mut dest_file)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,9 +31,12 @@ fn csr_to_config(hv: tools::utils::CsrConfig, ram_config: &mut RamConfig) {
|
|||
fn round_mem(src: u32) -> u32 {
|
||||
(src + 4095) & !4095
|
||||
}
|
||||
// Look for the largest "ram" block, which we'll treat as main memory
|
||||
// Look for the largest memory block, which we'll treat as main memory
|
||||
for (k, v) in &hv.regions {
|
||||
if k.find("ram").is_some() && v.length > ram_config.size {
|
||||
if (
|
||||
k.find("sram").is_some() // uniquely finds region on Precursor and Cramium (excludes 'reram' correctly)
|
||||
|| k.find("ddr_ram").is_some() // finds region on atsama5d27 uniquely
|
||||
) && v.length > ram_config.size {
|
||||
ram_config.size = round_mem(v.length);
|
||||
ram_config.offset = v.start;
|
||||
found_ram_name = Some(k.clone());
|
||||
|
|
@ -41,12 +44,13 @@ fn csr_to_config(hv: tools::utils::CsrConfig, ram_config: &mut RamConfig) {
|
|||
}
|
||||
|
||||
if found_ram_name.is_none() {
|
||||
eprintln!("Error: Couldn't find a memory region named \"ram\" in config file");
|
||||
eprintln!("Error: Couldn't find a memory region named \"sram\" in config file");
|
||||
return;
|
||||
}
|
||||
|
||||
// Now that we know which block is ram, add the other regions.
|
||||
let found_ram_name = MemoryRegion::make_name(&found_ram_name.unwrap());
|
||||
let mut raw_regions = Vec::<MemoryRegion>::new();
|
||||
for (k, v) in &hv.regions {
|
||||
ram_config.memory_required += round_mem(v.length) / 4096;
|
||||
let region_name = MemoryRegion::make_name(k);
|
||||
|
|
@ -59,11 +63,22 @@ fn csr_to_config(hv: tools::utils::CsrConfig, ram_config: &mut RamConfig) {
|
|||
if round_mem(v.length) == 0 {
|
||||
continue;
|
||||
}
|
||||
ram_config
|
||||
.regions
|
||||
.add(MemoryRegion::new(v.start, round_mem(v.length), region_name));
|
||||
raw_regions.push(MemoryRegion::new(v.start, round_mem(v.length), region_name));
|
||||
}
|
||||
// condense adjacent regions & eliminate overlapping regions
|
||||
raw_regions.sort_by(|a, b| a.start.partial_cmp(&b.start).unwrap() );
|
||||
let mut candidate_region = raw_regions[0];
|
||||
for r in raw_regions[1..].iter() {
|
||||
if r.start > candidate_region.start + candidate_region.length {
|
||||
ram_config.regions.add(candidate_region);
|
||||
candidate_region = r.to_owned();
|
||||
} else {
|
||||
candidate_region.length = (r.start + r.length) - candidate_region.start;
|
||||
}
|
||||
}
|
||||
ram_config.regions.add(candidate_region);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
env_logger::init();
|
||||
let matches = App::new("Xous Image Creator")
|
||||
|
|
@ -113,10 +128,19 @@ fn main() {
|
|||
.short("s")
|
||||
.long("svd")
|
||||
.value_name("SOC_SVD")
|
||||
.help("soc.csv file from litex")
|
||||
.help("soc.svd file from litex (system-level)")
|
||||
.takes_value(true)
|
||||
.required_unless_one(&["ram", "svd", "csv"]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("extra-svd")
|
||||
.long("extra-svd")
|
||||
.value_name("EXTRA_SVD")
|
||||
.help("extra SVD files")
|
||||
.takes_value(true)
|
||||
.multiple(true)
|
||||
.number_of_values(1)
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("ram")
|
||||
.short("r")
|
||||
|
|
@ -184,7 +208,8 @@ fn main() {
|
|||
|
||||
if let Some(soc_svd) = matches.value_of("svd") {
|
||||
let soc_svd_file = std::path::Path::new(soc_svd);
|
||||
let desc = svd2utra::parse_svd(soc_svd_file).unwrap();
|
||||
let svd_file = std::fs::File::open(soc_svd_file).unwrap();
|
||||
let desc = svd2utra::parse_svd(vec![svd_file]).unwrap();
|
||||
let mut map = std::collections::BTreeMap::new();
|
||||
|
||||
let mut csr_top = 0;
|
||||
|
|
@ -224,6 +249,42 @@ fn main() {
|
|||
);
|
||||
}
|
||||
}
|
||||
// insert additional regions from core.svd, if specified
|
||||
if matches.is_present("extra-svd") {
|
||||
let files: Vec<_> = matches.values_of("extra-svd").unwrap().collect();
|
||||
for soc_svd in files {
|
||||
let soc_svd_file = std::path::Path::new(soc_svd);
|
||||
let svd_file = std::fs::File::open(soc_svd_file).unwrap();
|
||||
let desc = svd2utra::parse_svd(vec![svd_file]).unwrap();
|
||||
|
||||
let mut csr_top = 0;
|
||||
for peripheral in desc.peripherals {
|
||||
if peripheral.base > csr_top {
|
||||
csr_top = peripheral.base;
|
||||
}
|
||||
}
|
||||
for mut region in desc.memory_regions {
|
||||
loop {
|
||||
if map.contains_key(®ion.name.to_lowercase()) {
|
||||
let mut new_name = region.name.to_lowercase();
|
||||
new_name.push_str("_");
|
||||
region.name = new_name;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
println!("{}: {:x}", region.name, region.base);
|
||||
map.insert(
|
||||
region.name.to_lowercase(),
|
||||
tools::utils::CsrMemoryRegion {
|
||||
start: region.base.try_into().unwrap(),
|
||||
length: region.size.try_into().unwrap(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
csr_to_config(tools::utils::CsrConfig { regions: map }, &mut ram_config);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,16 +11,16 @@ macro_rules! make_type {
|
|||
}};
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
pub struct MemoryRegion {
|
||||
/// Starting offset (in bytes)
|
||||
start: u32,
|
||||
pub start: u32,
|
||||
|
||||
/// Length (in bytes)
|
||||
length: u32,
|
||||
pub length: u32,
|
||||
|
||||
/// Region name (as a type)
|
||||
name: XousArgumentCode,
|
||||
pub name: XousArgumentCode,
|
||||
|
||||
/// Unused
|
||||
_padding: u32,
|
||||
|
|
|
|||
6
utralib/Cargo.lock
generated
6
utralib/Cargo.lock
generated
|
|
@ -19,14 +19,16 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "svd2utra"
|
||||
version = "0.1.17"
|
||||
version = "0.1.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ba87b81de81a553ec5904993a676aa29d1583a4f6ab96e03f8b44e4d6c05a61"
|
||||
dependencies = [
|
||||
"quick-xml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utralib"
|
||||
version = "0.1.19"
|
||||
version = "0.1.20"
|
||||
dependencies = [
|
||||
"svd2utra",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -96,27 +96,27 @@ fn main() {
|
|||
|
||||
// ----- select an SVD file based on a specific revision -----
|
||||
#[cfg(feature = "precursor-perflib")]
|
||||
let svd_filename = "precursor/soc-perf.svd";
|
||||
let svd_filenames = vec!["precursor/soc-perf.svd"];
|
||||
#[cfg(feature = "precursor-perflib")]
|
||||
let generated_filename = "src/generated/precursor_perf.rs";
|
||||
|
||||
#[cfg(feature = "renode")]
|
||||
let svd_filename = "renode/renode.svd";
|
||||
let svd_filenames = vec!["renode/renode.svd"];
|
||||
#[cfg(feature = "renode")]
|
||||
let generated_filename = "src/generated/renode.rs";
|
||||
|
||||
#[cfg(feature = "precursor-dvt")]
|
||||
let svd_filename = "precursor/soc-dvt.svd";
|
||||
let svd_filenames = vec!["precursor/soc-dvt.svd"];
|
||||
#[cfg(feature = "precursor-dvt")]
|
||||
let generated_filename = "src/generated/precursor_dvt.rs";
|
||||
|
||||
#[cfg(feature = "precursor-pvt")]
|
||||
let svd_filename = "precursor/soc-pvt.svd";
|
||||
let svd_filenames = vec!["precursor/soc-pvt.svd"];
|
||||
#[cfg(feature = "precursor")]
|
||||
let generated_filename = "src/generated/precursor_pvt.rs";
|
||||
|
||||
#[cfg(feature = "atsama5d27")]
|
||||
let svd_filename = "atsama5d/ATSAMA5D27.svd";
|
||||
let svd_filenames = vec!["atsama5d/ATSAMA5D27.svd"];
|
||||
#[cfg(feature = "atsama5d27")]
|
||||
let generated_filename = "src/generated/atsama5d27.rs";
|
||||
|
||||
|
|
@ -128,15 +128,18 @@ fn main() {
|
|||
// $env:CARGO_LOG="cargo::core::compiler::fingerprint=info"
|
||||
#[cfg(not(feature = "hosted"))]
|
||||
{
|
||||
let svd_file_path = std::path::Path::new(&svd_filename);
|
||||
println!(
|
||||
"cargo:rerun-if-changed={}",
|
||||
svd_file_path.canonicalize().unwrap().display()
|
||||
);
|
||||
|
||||
let mut svd_files = Vec::new();
|
||||
for svd in svd_filenames.iter() {
|
||||
let svd_file_path = std::path::Path::new(&svd);
|
||||
println!(
|
||||
"cargo:rerun-if-changed={}",
|
||||
svd_file_path.canonicalize().unwrap().display()
|
||||
);
|
||||
svd_files.push(std::fs::File::open(svd_file_path).unwrap());
|
||||
}
|
||||
// Regenerate the utra file in RAM.
|
||||
let mut dest_vec = vec![];
|
||||
svd2utra::generate(svd_file_path, &mut dest_vec).unwrap();
|
||||
svd2utra::generate(svd_files, &mut dest_vec).unwrap();
|
||||
|
||||
// If the file exists, check to see if it is different from what we just generated.
|
||||
// If not, skip writing the new file.
|
||||
|
|
@ -171,7 +174,9 @@ fn main() {
|
|||
.truncate(true)
|
||||
.open(svd_path)
|
||||
.unwrap();
|
||||
write!(svd_file, "utralib/{}", svd_filename).unwrap();
|
||||
for svd in svd_filenames.iter() {
|
||||
writeln!(svd_file, "utralib/{}", svd).unwrap();
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "hosted")]
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use std::{
|
|||
path::{Path, PathBuf},
|
||||
process::Command,
|
||||
};
|
||||
use crate::{DynError, app_manifest::generate_app_menus, MemorySpec};
|
||||
use crate::{DynError, app_manifest::generate_app_menus};
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
|
@ -674,8 +674,12 @@ impl Builder {
|
|||
let mut svd_spec_file = OpenOptions::new()
|
||||
.read(true)
|
||||
.open(&svd_spec_path)?;
|
||||
let mut svd_path = String::new();
|
||||
svd_spec_file.read_to_string(&mut svd_path)?;
|
||||
let mut svd_path_str = String::new();
|
||||
svd_spec_file.read_to_string(&mut svd_path_str)?;
|
||||
let mut svd_paths = Vec::new();
|
||||
for line in svd_path_str.lines() {
|
||||
svd_paths.push(line.to_owned());
|
||||
}
|
||||
|
||||
// ---------- install any pre-built packages ----------
|
||||
services_path.append(&mut self.fetch_prebuilds()?);
|
||||
|
|
@ -687,7 +691,7 @@ impl Builder {
|
|||
&kernel_path[0],
|
||||
&inie,
|
||||
&inif,
|
||||
MemorySpec::SvdFile(svd_path)
|
||||
svd_paths,
|
||||
)?;
|
||||
println!();
|
||||
println!(
|
||||
|
|
@ -780,7 +784,7 @@ impl Builder {
|
|||
kernel: &String,
|
||||
init: &[String],
|
||||
inif: &[String],
|
||||
memory_spec: MemorySpec,
|
||||
memory_spec: Vec::<String>,
|
||||
) -> Result<PathBuf, DynError> {
|
||||
let stream = self.stream.to_str();
|
||||
let mut args = vec!["run", "--package", "tools", "--bin", "create-image", "--"];
|
||||
|
|
@ -805,10 +809,15 @@ impl Builder {
|
|||
args.push(i);
|
||||
}
|
||||
|
||||
match memory_spec {
|
||||
MemorySpec::SvdFile(ref s) => {
|
||||
args.push("--svd");
|
||||
args.push(s);
|
||||
if memory_spec.len() == 1 {
|
||||
args.push("--svd");
|
||||
args.push(&memory_spec[0])
|
||||
} else {
|
||||
args.push("--svd");
|
||||
args.push(&memory_spec[0]);
|
||||
for spec in memory_spec[1..].iter() {
|
||||
args.push("--extra-svd");
|
||||
args.push(&spec);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -486,10 +486,6 @@ Note: By default, the `ticktimer` will get rebuilt every time. You can skip this
|
|||
|
||||
type DynError = Box<dyn std::error::Error>;
|
||||
|
||||
enum MemorySpec {
|
||||
SvdFile(String),
|
||||
}
|
||||
|
||||
/// [cratespecs] are positional arguments, and is a list of 0 to N tokens that immediately
|
||||
/// follow [verb]
|
||||
fn get_cratespecs() -> Vec<String> {
|
||||
|
|
|
|||
Loading…
Reference in a new issue