Skip to content

Commit 11aa2d5

Browse files
committed
refactor(test_runner): support more diverse linker script configurations
1 parent 00ae56a commit 11aa2d5

File tree

11 files changed

+142
-86
lines changed

11 files changed

+142
-86
lines changed

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/r3_test_runner/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ probe-rs-rtt = { version = "0.11.0" }
1515
tokio-serial = { version = "5.4.1" }
1616
lazy_static = { version = "1.4.0" }
1717
env_logger = { version = "0.8.4" }
18+
fn-formats = { version = "0.0.5" }
1819
serde_json = { version = "1.0.57" }
1920
serialport = { version = "4.0.0" }
2021
itertools = { version = "0.10.0" }

src/r3_test_runner/src/driverinterface.rs

Lines changed: 22 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -102,45 +102,30 @@ impl TestDriver {
102102
target_arch_opt: targets::BuildOpt,
103103
) -> impl Future<Output = Result<Self, TestDriverNewError>> {
104104
// Choose the right test driver for the given target architecture
105-
let (crate_name, rustflags_linkarg) = match target_arch {
106-
targets::Arch::Armv7A => (
107-
"r3_port_arm_test_driver",
108-
"-C link-arg=-Tlink_ram_harvard.x",
109-
),
110-
targets::Arch::ArmM { .. } => ("r3_port_arm_m_test_driver", "-C link-arg=-Tlink.x"),
111-
targets::Arch::Riscv { .. } => (
112-
"r3_port_riscv_test_driver",
113-
"-C link-arg=-Tmemory.x -C link-arg=-Tlink.x",
114-
),
105+
let crate_name = match target_arch {
106+
targets::Arch::Armv7A => "r3_port_arm_test_driver",
107+
targets::Arch::ArmM { .. } => "r3_port_arm_m_test_driver",
108+
targets::Arch::Riscv { .. } => "r3_port_riscv_test_driver",
115109
};
116110

117111
// Locate the test driver's crate
118112
let crate_path = driver_base_path.join(crate_name);
119113
log::debug!("driver.crate_name = {:?}", crate_name);
120-
log::debug!("driver.rustflags_linkarg = {:?}", rustflags_linkarg);
121114
log::debug!("driver.crate_path = {:?}", crate_path);
122115

123116
async move {
124117
if !crate_path.is_dir() {
125118
return Err(TestDriverNewError::BadDriverPath(crate_path));
126119
}
127120

128-
Self::new_inner(
129-
crate_path,
130-
target,
131-
crate_name,
132-
rustflags_linkarg,
133-
target_arch_opt,
134-
)
135-
.await
121+
Self::new_inner(crate_path, target, crate_name, target_arch_opt).await
136122
}
137123
}
138124

139125
async fn new_inner(
140126
crate_path: PathBuf,
141127
target: &'static dyn targets::Target,
142128
crate_name: &'static str,
143-
rustflags_linkarg: &'static str,
144129
target_arch_opt: targets::BuildOpt,
145130
) -> Result<Self, TestDriverNewError> {
146131
// Move to the driver directory
@@ -194,20 +179,30 @@ impl TestDriver {
194179
.join(crate_name);
195180
log::debug!("exe_path = '{}'", exe_path.display());
196181

197-
// Put the linker script in a directory
182+
// Put generated linker scripts in a directory
183+
let linker_scripts = target.linker_scripts();
184+
log::debug!("linker_scripts = {:?}", linker_scripts);
198185
let link_dir =
199186
tempdir::TempDir::new("r3_test_runner").map_err(TestDriverNewError::TempDirError)?;
200-
{
201-
let memory_x_path = link_dir.path().join("memory.x");
202-
log::debug!("Writing '{}'", memory_x_path.display());
203-
std::fs::write(&memory_x_path, target.memory_layout_script())
204-
.map_err(|e| TestDriverNewError::WriteError(memory_x_path, e))?;
187+
188+
for (name, contents) in linker_scripts.generated_files {
189+
let generated_file_path = link_dir.path().join(name);
190+
log::debug!("Writing '{}'", generated_file_path.display());
191+
std::fs::write(&generated_file_path, contents)
192+
.map_err(|e| TestDriverNewError::WriteError(generated_file_path, e))?;
205193
}
206194

195+
let rustflags_linkarg = fn_formats::DisplayFmt(|f| {
196+
for name in linker_scripts.inputs.iter() {
197+
write!(f, " -C link-arg=-T{}", name)?;
198+
}
199+
Ok(())
200+
});
201+
207202
// Derive `RUSTFLAGS`.
208203
let target_features = &target_arch_opt.target_features;
209204
let rustflags = if target_features.is_empty() {
210-
rustflags_linkarg.to_owned()
205+
rustflags_linkarg.to_string()
211206
} else {
212207
format!(
213208
"{} -C target-feature={}",

src/r3_test_runner/src/targets.rs

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,55 @@ pub trait Target: Send + Sync {
2020
/// `r3_port_*_test_driver`.
2121
fn cargo_features(&self) -> Vec<String>;
2222

23-
/// Generate the `memory.x` file to be included by the linker script of
24-
/// `cortex-m-rt` or `r3_port_arm`, or to be used as the top-level
25-
/// linker script by `r3_port_riscv_test_driver`.
26-
fn memory_layout_script(&self) -> String;
23+
/// The linker scripts used to link the test driver.
24+
fn linker_scripts(&self) -> LinkerScripts;
2725

2826
/// Connect to the target.
2927
fn connect(&self) -> Pin<Box<dyn Future<Output = Result<Box<dyn DebugProbe>>>>>;
3028
}
3129

30+
#[derive(Debug)]
31+
pub struct LinkerScripts {
32+
/// Linker scripts to specify with `-C link-arg=-T...` options. Note that
33+
/// linker scripts may refer to others by `INCLUDE` directives, in which
34+
/// case the referenced scripts shouldn't be specified here.
35+
pub inputs: Vec<String>,
36+
/// Temporary linker scripts to generate.
37+
pub generated_files: Vec<(String, String)>,
38+
}
39+
40+
impl LinkerScripts {
41+
/// Create `LinkerScripts` to use the `link_ram_harvard.x` provided by
42+
/// `r3_port_arm`. The specified string is written to `memory.x`, which will
43+
/// be imported by `link_ram_harvard.x`.
44+
fn arm_harvard(memory_definition: String) -> Self {
45+
Self {
46+
inputs: vec!["link_ram_harvard.x".to_owned()],
47+
generated_files: vec![("memory.x".to_owned(), memory_definition)],
48+
}
49+
}
50+
51+
/// Create `LinkerScripts` to use the `link.x` provided by `cortex-m-rt`.
52+
/// The specified string is written to `memory.x`, which will be imported by
53+
/// `link_ram_harvard.x`.
54+
fn arm_m_rt(memory_definition: String) -> Self {
55+
Self {
56+
inputs: vec!["link.x".to_owned()],
57+
generated_files: vec![("memory.x".to_owned(), memory_definition)],
58+
}
59+
}
60+
61+
/// Create `LinkerScripts` to use the `link.x` provided by `riscv-rt`.
62+
/// The specified string is written to `memory.x`, which defines memory
63+
/// regions referenced by `link.x`.
64+
fn riscv_rt(memory_definition: String) -> Self {
65+
Self {
66+
inputs: vec!["memory.x".to_owned(), "link.x".to_owned()],
67+
generated_files: vec![("memory.x".to_owned(), memory_definition)],
68+
}
69+
}
70+
}
71+
3272
pub trait DebugProbe: Send {
3373
/// Program the specified ELF image and run it from the beginning to
3474
/// capture its output.
@@ -66,8 +106,8 @@ impl<T: Target> Target for OverrideTargetArch<T> {
66106
self.1.cargo_features()
67107
}
68108

69-
fn memory_layout_script(&self) -> String {
70-
self.1.memory_layout_script()
109+
fn linker_scripts(&self) -> LinkerScripts {
110+
self.1.linker_scripts()
71111
}
72112

73113
fn connect(&self) -> Pin<Box<dyn Future<Output = Result<Box<dyn DebugProbe>>>>> {

src/r3_test_runner/src/targets/jlink.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use tokio::{
1515
task::spawn_blocking,
1616
};
1717

18-
use super::{Arch, DebugProbe, DynAsyncRead, Target};
18+
use super::{Arch, DebugProbe, DynAsyncRead, LinkerScripts, Target};
1919
use crate::subprocess;
2020

2121
/// SparkFun RED-V RedBoard or Things Plus
@@ -36,8 +36,9 @@ impl Target for RedV {
3636
]
3737
}
3838

39-
fn memory_layout_script(&self) -> String {
40-
r#"
39+
fn linker_scripts(&self) -> LinkerScripts {
40+
LinkerScripts::riscv_rt(
41+
r#"
4142
MEMORY
4243
{
4344
FLASH : ORIGIN = 0x20000000, LENGTH = 16M
@@ -55,10 +56,10 @@ impl Target for RedV {
5556
_stext = 0x20010000;
5657
5758
_hart_stack_size = 1K;
58-
"#
59-
.to_owned()
59+
"#
60+
.to_owned(),
61+
)
6062
}
61-
6263
fn connect(&self) -> Pin<Box<dyn Future<Output = Result<Box<dyn DebugProbe>>>>> {
6364
Box::pin(std::future::ready(Ok(Box::new(Fe310JLinkDebugProbe) as _)))
6465
}

src/r3_test_runner/src/targets/kflash.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use tokio_serial::{SerialPort, SerialPortBuilderExt, SerialStream};
1313
use super::{
1414
demux::Demux,
1515
serial::{choose_serial, ChooseSerialError},
16-
slip, Arch, DebugProbe, DynAsyncRead, Target,
16+
slip, Arch, DebugProbe, DynAsyncRead, LinkerScripts, Target,
1717
};
1818
use crate::utils::retry_on_fail;
1919

@@ -35,8 +35,9 @@ impl Target for Maix {
3535
]
3636
}
3737

38-
fn memory_layout_script(&self) -> String {
39-
r#"
38+
fn linker_scripts(&self) -> LinkerScripts {
39+
LinkerScripts::riscv_rt(
40+
r#"
4041
MEMORY
4142
{
4243
RAM : ORIGIN = 0x80000000, LENGTH = 6M
@@ -50,10 +51,10 @@ impl Target for Maix {
5051
REGION_ALIAS("REGION_STACK", RAM);
5152
5253
_hart_stack_size = 1K;
53-
"#
54-
.to_owned()
54+
"#
55+
.to_owned(),
56+
)
5557
}
56-
5758
fn connect(&self) -> Pin<Box<dyn Future<Output = Result<Box<dyn DebugProbe>>>>> {
5859
Box::pin(async { KflashDebugProbe::new().await.map(|x| Box::new(x) as _) })
5960
}

src/r3_test_runner/src/targets/openocd.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use tokio::{
1212
process::Child,
1313
};
1414

15-
use super::{Arch, DebugProbe, DynAsyncRead, Target};
15+
use super::{Arch, DebugProbe, DynAsyncRead, LinkerScripts, Target};
1616
use crate::subprocess;
1717

1818
/// GR-PEACH
@@ -27,17 +27,18 @@ impl Target for GrPeach {
2727
vec!["board-rza1".to_owned()]
2828
}
2929

30-
fn memory_layout_script(&self) -> String {
31-
"
30+
fn linker_scripts(&self) -> LinkerScripts {
31+
LinkerScripts::arm_harvard(
32+
"
3233
MEMORY
3334
{
3435
RAM_CODE : ORIGIN = 0x20000000, LENGTH = 5120K
3536
RAM_DATA : ORIGIN = 0x20500000, LENGTH = 5120K
3637
}
37-
"
38-
.to_owned()
38+
"
39+
.to_owned(),
40+
)
3941
}
40-
4142
fn connect(&self) -> Pin<Box<dyn Future<Output = Result<Box<dyn DebugProbe>>>>> {
4243
Box::pin(async { Ok(Box::new(GrPeachOpenOcdDebugProbe::new()) as Box<dyn DebugProbe>) })
4344
}

src/r3_test_runner/src/targets/probe_rs.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use tokio::{
1616
time::{sleep, Sleep},
1717
};
1818

19-
use super::{Arch, DebugProbe, DynAsyncRead, Target};
19+
use super::{Arch, DebugProbe, DynAsyncRead, LinkerScripts, Target};
2020

2121
pub struct NucleoF401re;
2222

@@ -29,8 +29,9 @@ impl Target for NucleoF401re {
2929
vec!["output-rtt".to_owned()]
3030
}
3131

32-
fn memory_layout_script(&self) -> String {
33-
"
32+
fn linker_scripts(&self) -> LinkerScripts {
33+
LinkerScripts::arm_m_rt(
34+
"
3435
MEMORY
3536
{
3637
/* NOTE K = KiBi = 1024 bytes */
@@ -42,10 +43,10 @@ impl Target for NucleoF401re {
4243
/* The stack is of the full descending type. */
4344
/* NOTE Do NOT modify `_stack_start` unless you know what you are doing */
4445
_stack_start = ORIGIN(RAM) + LENGTH(RAM);
45-
"
46-
.to_owned()
46+
"
47+
.to_owned(),
48+
)
4749
}
48-
4950
fn connect(&self) -> Pin<Box<dyn Future<Output = Result<Box<dyn DebugProbe>>>>> {
5051
Box::pin(async {
5152
spawn_blocking(|| {

0 commit comments

Comments
 (0)