From d8b77db2b73c06610fb812200ff7652074c0d36c Mon Sep 17 00:00:00 2001 From: Verneri Hirvonen Date: Fri, 30 May 2025 14:14:02 +0300 Subject: [PATCH 1/3] run_rtl: return simulator stdout and stderr outputs The simulator output logs are currently printed to the terminal, but not stored anywhere. In A-Core simulations, UART prints are output over STDERR stream. In order to post-process the values printed over serial, there needs to be a way to access the simulation logs from python. This commit changes run_rtl to return the stdout and stderr streams, so that they are accessible in rtl testbenches. --- rtl/__init__.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/rtl/__init__.py b/rtl/__init__.py index 9d32d90..5d48c45 100644 --- a/rtl/__init__.py +++ b/rtl/__init__.py @@ -924,6 +924,8 @@ def execute_rtl_sim(self): Add the probes in the simulation as you wish. To finish the simulation, run the simulation to end and exit.""") + stdout = None + stderr = None try: if self.workdir: self.print_log(type='I', msg=f"Executing in directory {self.workdir}") @@ -933,13 +935,14 @@ def execute_rtl_sim(self): execpath=self.rtlsimpath self.print_log(type='I', msg="Running external command %s\n" %(self.rtlcmd) ) rtlcmd = f"cd {execpath} && {self._rtlcmd}" - output = subprocess.check_output(rtlcmd, shell=True) - self.print_log(type='I', msg='Simulator output:\n'+output.decode('utf-8')) + proc = subprocess.Popen(self._rtlcmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout = proc.stdout.read().decode('utf-8') + stderr = proc.stderr.read().decode('utf-8') + self.print_log(type='I', msg='Simulator output:\n'+stdout) except subprocess.CalledProcessError as e: output = e.output self.print_log(type='F', msg='Simulator output:\n'+output.decode('utf-8')) - count=0 files_ok=False while not files_ok: @@ -952,6 +955,9 @@ def execute_rtl_sim(self): files_ok=True files_ok=files_ok and os.path.isfile(file.file) + # return simulator stdout and stderr logs for later analysis + return (stdout, stderr) + @property def assignment_matchlist(self): @@ -1024,7 +1030,7 @@ def run_rtl(self): self.tb.generate_contents() self.tb.export(force=True) self.write_infile() - self.execute_rtl_sim() + (stdout, stderr) = self.execute_rtl_sim() self.read_outfile() self.connect_outputs() # Save entity state @@ -1034,6 +1040,7 @@ def run_rtl(self): self.delete_iofile_bundle() self.delete_rtlworkpath() self.delete_rtlsimpath() + return (stdout, stderr) #This writes all infile def write_infile(self): From 95015aab8d705aa6f2fc6eadb5bc203927a71333 Mon Sep 17 00:00:00 2001 From: Verneri Hirvonen Date: Mon, 2 Jun 2025 13:10:30 +0300 Subject: [PATCH 2/3] Print out simulator stdout and stderr while capturing Last commit added support for capturing simulator textual outputs, but removed simulator output prints. This commit adds support for printing out simulator stdout and stderr while capturing the output. stdout and stderr are captured and printed one utf-8 code point at a time instead of byte-by-byt to support real-time printing of unicode symbols with multi-byte encoding. --- rtl/__init__.py | 45 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/rtl/__init__.py b/rtl/__init__.py index 5d48c45..589107e 100644 --- a/rtl/__init__.py +++ b/rtl/__init__.py @@ -21,6 +21,8 @@ from functools import reduce import shutil import re +import select +import io #TheSyDeKick modules if not (os.path.abspath('../../thesdk') in sys.path): @@ -924,8 +926,8 @@ def execute_rtl_sim(self): Add the probes in the simulation as you wish. To finish the simulation, run the simulation to end and exit.""") - stdout = None - stderr = None + stdout = "" + stderr = "" try: if self.workdir: self.print_log(type='I', msg=f"Executing in directory {self.workdir}") @@ -936,9 +938,42 @@ def execute_rtl_sim(self): self.print_log(type='I', msg="Running external command %s\n" %(self.rtlcmd) ) rtlcmd = f"cd {execpath} && {self._rtlcmd}" proc = subprocess.Popen(self._rtlcmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout = proc.stdout.read().decode('utf-8') - stderr = proc.stderr.read().decode('utf-8') - self.print_log(type='I', msg='Simulator output:\n'+stdout) + + # recode output streams to utf-8 so that we can read single unicode code points at a time. This should + # produce correct output character if a single character is encoded as multiple bytes. + try: + utf8_stdout = io.TextIOWrapper(proc.stdout, encoding='utf-8') + utf8_stderr = io.TextIOWrapper(proc.stderr, encoding='utf-8') + except Exception as e: + self.print_log(type='F', msg=f"Could not open simulator output stream: {e}") + + # keep printing simulator output in real time until simulator process closes output streams + self.print_log(type='I', msg='Simulator output:\n') + while True: + # multiplex reads from stdout and stderr, print them, and append to respective variable + (rready, _, _) = select.select([utf8_stdout, utf8_stderr], [], [], 0.001) + for stream in rready: + # not the most efficient way to print out stuff, but we want to be able to see individual characters + # being printed out in real time, possibly without newlines, so print and flush one at a time. + codepoint = stream.read(1) + sys.stdout.write(codepoint) + sys.stdout.flush() + if stream == utf8_stdout: + stdout += codepoint + if stream == utf8_stderr: + stderr += codepoint + + # wait for simulator process to terminate + try: + status = proc.wait(timeout=1e-6) + self.print_log(type='I', msg=f"Simulator exited with status code {status}") + # read any remaining output + stdout += utf8_stdout.read() + stderr += utf8_stderr.read() + break + except subprocess.TimeoutExpired: + pass + except subprocess.CalledProcessError as e: output = e.output self.print_log(type='F', msg='Simulator output:\n'+output.decode('utf-8')) From 0b7c75fd795b5a091c4dbab35da22f54d5fbb2cd Mon Sep 17 00:00:00 2001 From: Verneri Hirvonen Date: Mon, 9 Jun 2025 12:42:25 +0300 Subject: [PATCH 3/3] Use right rtlcmd variable for specifying rtl simulation command --- rtl/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtl/__init__.py b/rtl/__init__.py index 589107e..a390eb8 100644 --- a/rtl/__init__.py +++ b/rtl/__init__.py @@ -937,7 +937,7 @@ def execute_rtl_sim(self): execpath=self.rtlsimpath self.print_log(type='I', msg="Running external command %s\n" %(self.rtlcmd) ) rtlcmd = f"cd {execpath} && {self._rtlcmd}" - proc = subprocess.Popen(self._rtlcmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + proc = subprocess.Popen(rtlcmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # recode output streams to utf-8 so that we can read single unicode code points at a time. This should # produce correct output character if a single character is encoded as multiple bytes.