Skip to content

Commit 2a8edea

Browse files
committed
Merge branch 'main' into Code_generation_fix
2 parents 3f708be + 29eba02 commit 2a8edea

15 files changed

+428
-21
lines changed

.devcontainer/Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ FROM ubuntu:24.04
22

33
# Install git, git-lfs, colorama and build tools
44
RUN echo "deb http://security.ubuntu.com/ubuntu focal-security main universe" > /etc/apt/sources.list.d/ubuntu-focal-sources.list && \
5-
apt-get update && apt-get -y install git git-lfs python3-colorama cmake g++ build-essential libncurses5 libusb-1.0-0 gdb && \
5+
apt-get update && apt-get -y install git git-lfs python3 python3-pip curl python3-colorama cmake g++ build-essential libncurses5 libusb-1.0-0 gdb && \
66
git lfs install
77

88
# Install cubeclt

.github/workflows/build.yml

+17-1
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,32 @@ jobs:
2929
outputs:
3030
build-artifact: ${{ steps.upload-build-artifact.outputs.artifact-id }}
3131
container:
32-
image: jmaralo/hyperloop-upv-firmware:0.3.0
32+
image: jmaralo/hyperloop-upv-firmware:latest
3333
steps:
3434
- name: Clone Project
3535
uses: actions/checkout@v3
3636
with:
3737
submodules: recursive
38+
- name: Configure
39+
run: |
40+
apt install -y python3.12-venv
41+
python3 -m venv .
42+
source bin/activate
43+
pip install -r requirements.txt
44+
shell: bash
3845
- name: Build
3946
run: |
47+
source bin/activate
4048
cmake --preset ${{ inputs.preset }} .
4149
cmake --build --preset ${{ inputs.preset }}
50+
shell: bash
51+
- name: Run Simulator Tests
52+
if: inputs.preset == 'simulator'
53+
run: |
54+
cd Tests
55+
echo "Running tests against $(cd .. && pwd)/out/build/simulator/latest.elf"
56+
set -e
57+
python3 test.py -uut="$(cd .. && pwd)/out/build/latest.elf"
4258
- name: Upload Build
4359
id: upload-build-artifact
4460
uses: actions/upload-artifact@v4

.gitignore

+5
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ _deps
3535
CMakeUserPresets.json
3636
.cache
3737

38+
# *-----------------------*
39+
# | Flash Dump Utilities |
40+
# *-----------------------*
41+
42+
tools/*.bin
3843
# *-----------------------*
3944
# | C/C++ |
4045
# *-----------------------*

.vscode/settings.json

+15-1
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,19 @@
55
"C_Cpp.formatting": "clangFormat",
66
"C_Cpp.clang_format_style": "{BasedOnStyle: Google, IndentWidth: 4, ColumnLimit: 80}",
77
"C_Cpp.clang_format_sortIncludes": true,
8-
"C_Cpp.intelliSenseCacheSize": 0
8+
"C_Cpp.intelliSenseCacheSize": 0,
9+
"files.associations": {
10+
"condition_variable": "cpp",
11+
"chrono": "cpp",
12+
"array": "cpp",
13+
"*.tcc": "cpp",
14+
"memory": "cpp",
15+
"format": "cpp",
16+
"istream": "cpp",
17+
"functional": "cpp",
18+
"tuple": "cpp",
19+
"utility": "cpp",
20+
"variant": "cpp",
21+
"atomic": "cpp"
22+
}
923
}

CMakeLists.txt

+12-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,17 @@ project (template-project ASM C CXX)
44
set(EXECUTABLE ${PROJECT_NAME}.elf)
55
set(STLIB_DIR ${CMAKE_CURRENT_LIST_DIR}/deps/ST-LIB)
66

7+
8+
add_custom_target(
9+
my_custom_target_that_always_runs ALL
10+
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/_tmp.h
11+
)
12+
add_custom_command(
13+
OUTPUT
14+
${CMAKE_CURRENT_BINARY_DIR}/_tmp.h # fake! ensure we run!
15+
COMMAND python3 ${CMAKE_SOURCE_DIR}/tools/generate_binary_metadata.py
16+
)
17+
718
option(USE_ETHERNET "Enable ethernet peripheral" OFF)
819
option(TARGET_NUCLEO "Targets the STM32H723 Nucleo development board" OFF)
920

@@ -30,6 +41,7 @@ set(HAL_HEADER
3041
)
3142
set(HAL_RUNES
3243
${CMAKE_SOURCE_DIR}/Core/Src/Runes/Runes.cpp
44+
${CMAKE_CURRENT_SOURCE_DIR}/Core/Src/Runes/generated_metadata.cpp
3345
)
3446
set(HAL_SOURCE
3547
${CMAKE_SOURCE_DIR}/Core/Src/stm32h7xx_hal_msp.c
@@ -148,7 +160,6 @@ target_link_options(${EXECUTABLE} PRIVATE
148160
$<$<BOOL:${CMAKE_CROSSCOMPILING}>:-Wl,-Map=${PROJECT_NAME}.map,--cref>
149161
$<$<BOOL:${CMAKE_CROSSCOMPILING}>:-Wl,--gc-sections>
150162
)
151-
152163
if (PROJECT_IS_TOP_LEVEL)
153164
# Create symlink to compile_commands.json for IDE to pick it up
154165
execute_process(

Core/Src/Runes/generated_metadata.cpp

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* AUTOGENERATED FILE
3+
* DO NOT MODIFY MANUALLY!!!
4+
*/
5+
extern "C"{
6+
const char DESCRIPTION[255] __attribute__((section(".metadata_pool")))=
7+
"****************" // placeholder for beggining
8+
"***************" // DateTime using ISO-8601 format
9+
" " // alignment
10+
"--------" // STLIB commit
11+
"--------" // ADJ commit
12+
"--------" // Board commit
13+
// the '=' is used for unparsing
14+
;
15+
}

Core/Src/main.cpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@ int main(void) {
1111
SharedMemory::start();
1212
#endif
1313

14-
DigitalOutput led_on(PA1);
14+
DigitalOutput led_on(PB1);
1515
STLIB::start();
1616

17-
Time::register_low_precision_alarm(100, [&]() { led_on.toggle(); });
17+
Time::register_low_precision_alarm(5, [&]() { led_on.toggle();
18+
});
1819

1920
while (1) {
2021
STLIB::update();

STM32H723ZGTX_FLASH.ld

+15-1
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,21 @@ SECTIONS
134134
. = ALIGN(4);
135135
_edata = .; /* define a global symbol at data end */
136136
} >RAM_D1 AT> FLASH
137-
137+
/*
138+
this needs to be the last thing in FLASH
139+
because the preceeding sections are appended after the one preceeding them
140+
this is, if this were the first thing in FLASH
141+
the sections below it would try to be placed afterwards
142+
thus overflowing the FLASH
143+
*/
144+
.metadata_pool :
145+
{
146+
. = ABSOLUTE(0x080DFD00);
147+
. = ALIGN(4);
148+
metadata = .;
149+
KEEP(*(.metadata_pool))
150+
. += 0x100;
151+
} >FLASH
138152
/* Uninitialized data section */
139153
. = ALIGN(4);
140154
.bss (NOLOAD) :

Tests/runner.py

+67-11
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,25 @@
11
import subprocess
22
from argparse import ArgumentParser
33
import time
4+
import sys
5+
import os
6+
from datetime import datetime
7+
import traceback
8+
9+
import logging
10+
import threading
11+
12+
pathlogs = 'testsLog'
13+
14+
def LOG(*args, mode= 'INFO'):
15+
print(args)
16+
if(mode == 'INFO'):
17+
logging.info(args)
18+
elif(mode == 'ERR'):
19+
logging.error(args)
20+
elif(mode=='ELF'):
21+
logging.info(f'<CPP>{args}')
22+
423

524

625
class DuplicatedTestError(Exception):
@@ -16,14 +35,27 @@ def __init__(self, executable):
1635
self._executable = executable
1736

1837
def __enter__(self):
38+
self._executable = f"stdbuf -oL {self._executable}"
1939
self._process = subprocess.Popen(
2040
self._executable,
2141
shell=True,
2242
stdout=subprocess.PIPE,
2343
stderr=subprocess.PIPE,
24-
text=True
44+
text=True,
45+
bufsize=1,
2546
)
2647

48+
self._stdout_thread = threading.Thread(target=self.stream_output)
49+
self._stdout_thread.daemon = True
50+
self._stdout_thread.start()
51+
52+
def stream_output(self):
53+
while True:
54+
line = self._process.stdout.readline()
55+
if not line: break
56+
57+
LOG(line, mode = 'ELF')
58+
2759
def __exit__(self, *args):
2860
try:
2961
out, err = self._process.communicate(timeout=1)
@@ -35,10 +67,12 @@ def __exit__(self, *args):
3567
out = "Error recovering stdout"
3668
err = "Error recovering stderr"
3769

70+
self._stdout_thread.join(timeout=1)
71+
3872
if out:
39-
print(f" * UUT stdout:\n{out}")
73+
LOG(f" * UUT stdout:\n{out}")
4074
if err:
41-
print(f" * UUT stderr:\n{err}")
75+
LOG(f" * UUT stderr:\n{err}")
4276

4377

4478
class Test:
@@ -99,27 +133,49 @@ def decorator(test_func):
99133

100134
# Runs all the registered tests, cleaning up after each test
101135
def run(self):
136+
137+
failed_tests = 0
138+
139+
if not os.path.exists(f'./{pathlogs}'):
140+
os.makedirs(f'./{pathlogs}')
141+
date = datetime.now()
142+
logging.basicConfig(level=logging.INFO, filename=f'{pathlogs}/{date}log.log', filemode='w', format="%(levelname)s - %(message)s")
143+
102144
for name, test in self._tests.items():
103145
try:
104146
test.run_prepare()
105147

106148
with self._uut:
107149
time.sleep(0.1)
108150
try:
109-
print(f"[{name}] Running...")
151+
LOG(f"[{name}] Running...")
110152
result = test()
111-
print(f"[{name}] Succesfull!")
153+
LOG(f"[{name}] Succesfull!")
112154
if result is not None:
113-
print(f" * Result: {result}")
155+
LOG(f" * Result: {result}")
114156
except Exception as reason:
115-
print(f"[{name}] Failed!")
116-
print(f" * Reason: {reason}")
157+
tb = traceback.extract_tb(reason.__traceback__)
158+
failed_tests += 1
159+
160+
# Obtener la última llamada (donde ocurrió el error)
161+
file1, line1, _, _ = tb[-2]
162+
file2, line2, _, _ = tb[-1]
163+
LOG(f"[{name}] Failed!", mode= 'ERR')
164+
LOG(f'file: {file1} line: {line1}, file: {file2} line: {line2}', mode = 'ERR')
165+
LOG(f" * Reason: {reason}", mode = 'ERR')
117166

118167
test.run_cleanup()
119168
except KeyboardInterrupt:
120-
print(f"[{name}] Keyboard Interrupt. Aborted.")
121-
122-
169+
LOG(f"[{name}] Keyboard Interrupt. Aborted.")
170+
import sys
171+
sys.exit(130)
172+
173+
if failed_tests>0:
174+
import sys
175+
LOG(f"[{failed_tests}] Tests Failed!", mode= 'ERR')
176+
sys.exit(1)
177+
178+
123179

124180
parser = ArgumentParser(
125181
prog="test",

Tests/test.py

+19-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import sys, os
22
sys.path.append(os.path.join(os.path.dirname(__file__), "VirtualMCU", "src"))
33

4-
from runner import runner
4+
from runner import runner, LOG
55
from vmcu.shared_memory import SharedMemory
66
from vmcu.pin import Pinout, DigitalOut
77
from vmcu.services.digital_out import DigitalOutService
@@ -10,11 +10,13 @@
1010

1111
@runner.test()
1212
def led_toggle():
13+
1314
TOGGLE_PERIOD = milliseconds(100 * 2)
14-
ALLOWED_SLACK = milliseconds(5)
15+
ALLOWED_SLACK = milliseconds(100)
1516

1617
shm = SharedMemory("gpio__blinking_led", "state_machine__blinking_led")
17-
led = DigitalOutService(shm, Pinout.PA1)
18+
led = DigitalOutService(shm, Pinout.PB1)
19+
state = DigitalOut.State.Low
1820

1921
def led_turns_on():
2022
nonlocal led
@@ -30,8 +32,20 @@ def led_turns_off():
3032
before=(TOGGLE_PERIOD / 2) + ALLOWED_SLACK,
3133
msg="Sync fails"
3234
)
35+
completes(
36+
wait_until_true(led_turns_off),
37+
before=(TOGGLE_PERIOD / 2) + ALLOWED_SLACK,
38+
msg="Sync fails"
39+
)
40+
completes(
41+
wait_until_true(led_turns_on),
42+
before=(TOGGLE_PERIOD / 2) + ALLOWED_SLACK,
43+
msg="Sync fails"
44+
)
45+
time_start = nanoseconds(time.time_ns())
3346

3447
for i in range(150):
48+
t = time.time_ns()
3549
completes(
3650
wait_until_true(led_turns_off),
3751
before=(TOGGLE_PERIOD / 2) + ALLOWED_SLACK,
@@ -44,7 +58,9 @@ def led_turns_off():
4458
after=(TOGGLE_PERIOD / 2) - ALLOWED_SLACK,
4559
msg="turns on"
4660
)
61+
time_start += TOGGLE_PERIOD
4762
print("toggle", i)
63+
print(time.time_ns()-t-TOGGLE_PERIOD*10**9)
4864

4965

5066
runner.run() # Runs the tests, do not delete!

0 commit comments

Comments
 (0)