Skip to content

Commit 380814d

Browse files
Milestone 0.4 - minor update to 0.4.1 (#90)
* fix scope trigger and enumeration type (#87) * removed MCU data width scaling, trigger point calculation for array element * reinserting get_variable_raw This method was refactored in version 0.1 to internal method _get_variable_instance. VariableInfo was moved from Parser class to Variable class. The example SFR_Example.py was made to test this behavior but was not included into the tests. It is not yet included. * refactored some examples to fix logging * fix SFR example to import the right library * change get_device_id example * Fixed enum variable types (static only) inside structures still not working * Enhancing enum implementation with architecture considerations (data width) * fixed import error on LNet the code was importing the module and should import the class. --------- Co-authored-by: Edras Pacola <edras.pacola@microchip.com> * bump version to 0.4.1. --------- Co-authored-by: X2Cscope <73351552+X2Cscope@users.noreply.github.com>
1 parent a34783a commit 380814d

File tree

11 files changed

+219
-72
lines changed

11 files changed

+219
-72
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
44

55
[tool.poetry]
66
name = "pyx2cscope"
7-
version = "0.4.0"
7+
version = "0.4.1"
88
description = "python implementation of X2Cscope"
99
authors = [
1010
"Yash Agarwal <yash.agarwal@microchip.com>",

pyx2cscope/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
"""This module contains the pyx2cscope package.
22
3-
Version: 0.4.0
3+
Version: 0.4.1
44
"""
55

66
import logging
77

8-
__version__ = "0.4.0"
8+
__version__ = "0.4.1"
99

1010

1111
def set_logger(

pyx2cscope/examples/SFR_Example.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from pyx2cscope.utils import get_com_port, get_elf_file_path
77
from pyx2cscope.x2cscope import X2CScope
8+
from variable.variable import VariableInfo
89

910
# Configuration for serial port communication
1011
serial_port = get_com_port() # Select COM port
@@ -37,7 +38,7 @@ def set_led_state(value, bit_position, state):
3738
return value
3839

3940

40-
def sethigh(value, bit_position):
41+
def set_high(value, bit_position):
4142
"""Set a specific bit to high (1).
4243
4344
Args:
@@ -50,7 +51,7 @@ def sethigh(value, bit_position):
5051
return set_led_state(value, bit_position, True)
5152

5253

53-
def setlow(value, bit_position):
54+
def set_low(value, bit_position):
5455
"""Set a specific bit to low (0).
5556
5657
Args:
@@ -67,9 +68,8 @@ def example():
6768
"""Main function to demonstrate LED state changes using SFR."""
6869
try:
6970
# Initialize the variable for the Special Function Register (SFR) controlling the LEDs
70-
sfr_led = x2cscope.get_variable_raw(
71-
3702, "int", "sfr"
72-
) # LATE address from data sheet 3702
71+
variable_info = VariableInfo("my_led", "int", 1, 3702, 0, {})
72+
sfr_led = x2cscope.get_variable_raw(variable_info) # LATE address from data sheet 3702
7373

7474
# Get the initial LED state from the SFR
7575
initial_led_state = sfr_led.get_value()
@@ -79,23 +79,23 @@ def example():
7979
#########################
8080
# SET LED1 and LED2 High
8181
##########################
82-
led1_high_value = sethigh(initial_led_state, LED1_BIT)
82+
led1_high_value = set_high(initial_led_state, LED1_BIT)
8383
sfr_led.set_value(led1_high_value)
8484

8585
initial_led_state = sfr_led.get_value()
86-
led2_high_value = sethigh(initial_led_state, LED2_BIT)
86+
led2_high_value = set_high(initial_led_state, LED2_BIT)
8787
sfr_led.set_value(led2_high_value)
8888

8989
#########################
9090
# SET LED1 and LED2 LOW
9191
##########################
9292
time.sleep(1)
9393
initial_led_state = sfr_led.get_value()
94-
led1_low_value = setlow(initial_led_state, LED1_BIT)
94+
led1_low_value = set_low(initial_led_state, LED1_BIT)
9595
sfr_led.set_value(led1_low_value)
9696

9797
initial_led_state = sfr_led.get_value()
98-
led2_low_value = setlow(initial_led_state, LED2_BIT)
98+
led2_low_value = set_low(initial_led_state, LED2_BIT)
9999
sfr_led.set_value(led2_low_value)
100100
time.sleep(1)
101101

pyx2cscope/examples/demo.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
x2cscope.add_scope_channel(phase_voltage)
1313

1414
x2cscope.request_scope_data()
15+
1516
while(1):
1617
if x2cscope.is_scope_data_ready():
1718
print(x2cscope.get_scope_channel_data())

pyx2cscope/examples/exampleMCAF.py

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,13 @@
11
"""exampleMCAF.py.
22
3-
this example is the very basic example to retrieve the value of a certain variable for motorBench generated code.
3+
this example is a basic example to retrieve the value of a variable from motorBench auto-generated code.
44
"""
55

6-
import logging
7-
86
from pyx2cscope.utils import get_com_port, get_elf_file_path
97
from pyx2cscope.x2cscope import X2CScope
108

11-
# Configure logging settings to capture all levels of log messages and write them to a file
12-
logging.basicConfig(
13-
level=0, # Log all levels of messages
14-
filename="BlinkyMCFG.log", # Log file name
15-
)
16-
179
# Configuration for serial port communication
18-
serial_port = get_com_port() # Get the COM port to use from the utility function
10+
serial_port = get_com_port() # Get the COM port from the utility function
1911
baud_rate = 115200 # Set baud rate for serial communication
2012

2113
# Specify the path to the ELF file
@@ -35,13 +27,8 @@
3527

3628
# Attempt to read the value of the 'speedReference' variable and log the result
3729
try:
38-
speed_reference_value = (
39-
speed_reference.get_value()
40-
) # Read the value of the speed reference variable
41-
logging.debug(
42-
f"Speed Reference Value: {speed_reference_value}"
43-
) # Log the retrieved value
30+
# Read the value of the speed reference variable
31+
speed_reference_value = speed_reference.get_value()
32+
print(f"Speed Reference Value: {speed_reference_value}")
4433
except Exception as e:
45-
logging.debug(
46-
f"Error reading speed reference value: {e}"
47-
) # Log any exceptions that occur
34+
print(f"Error reading speed reference value: {e}")
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
"""The simplest usage of the pyX2Cscope library without using and elf_file on the constructor.
2+
3+
The script initializes the X2CScope class with a specified serial port without ELF file.
4+
At this way, we don't know which variables are available on the processor, but we can
5+
get connection info, processor id, and x2cscope version.
6+
7+
It is possible to import later and elf file or a variable import file as .yml/.pkl
8+
In this way you can read variables available at flashed programm. Or use the method
9+
x2c_scope.get_variable_raw(VariableInfo var_info) to retrieve variables directly from the
10+
device's memory.
11+
"""
12+
13+
from pyx2cscope.x2cscope import X2CScope
14+
from variable.variable import VariableInfo
15+
16+
# initialize the X2CScope class with serial port, by default baud rate is 115200
17+
x2c_scope = X2CScope(port="COM32")
18+
19+
# Read device_info
20+
device_info = x2c_scope.get_device_info()
21+
22+
# Print the controller info
23+
# i.e.: {
24+
# 'processor_id': '__GENERIC_MICROCHIP_PIC32__',
25+
# 'uc_width': '32-bit',
26+
# 'date': 'Mar 32019',
27+
# 'time': '1220',
28+
# 'AppVer': 1,
29+
# 'dsp_state': 'Application runs on target'
30+
# }
31+
print(device_info)
32+
33+
# set the elf_file later, post instantiation to get some variables
34+
x2c_scope.import_variables("""your_path_to_elf_file.elf""")
35+
36+
# Collect some variables, i.e.: from QSPIN on SAME54 MCLV-48V-300W
37+
angle_reference = x2c_scope.get_variable("mcFocI_ModuleData_gds.dOutput.elecAngle")
38+
speed_measured = x2c_scope.get_variable("mcFocI_ModuleData_gds.dOutput.elecSpeed")
39+
40+
# or load a variable directly from the memory
41+
speed_measured_info = VariableInfo("speed_measured", "float", 2, 536879832, 0, {})
42+
speed_measured_raw = x2c_scope.get_variable_raw(speed_measured_info)
43+
44+
# Read the value of the "motor.apiData.velocityMeasured" variable from the target
45+
print(speed_measured.get_value())
46+
47+

pyx2cscope/parser/elf_parser.py

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,9 @@
88
"""
99
from abc import ABC, abstractmethod
1010

11-
from dataclasses import dataclass
1211
from typing import Dict, List, Optional
1312

14-
15-
@dataclass
16-
class VariableInfo:
17-
"""A data class representing information about a variable in an ELF file.
18-
19-
Attributes:
20-
name (str): The name of the variable.
21-
type (str): The data type of the variable.
22-
byte_size (int): The size of the variable in bytes.
23-
address (int): The memory address of the variable.
24-
array_size (int): The size of the array if the variable is an array, default is 0.
25-
"""
26-
27-
name: str
28-
type: str
29-
byte_size: int
30-
address: int
31-
array_size: int
32-
valid_values: Dict[str, int]
33-
13+
from pyx2cscope.variable.variable import VariableInfo
3414

3515
class ElfParser(ABC):
3616
"""Abstract base class for parsing ELF files.

pyx2cscope/parser/generic_parser.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -75,15 +75,14 @@ def _get_die_variable_details(self, die_variable):
7575
# variables, so this step may be avoided here.
7676
# We let the code here in case we want to process them anyway.
7777
# elif (
78-
# die_variable.attributes.get("DW_AT_external")
79-
# and die_variable.attributes.get("DW_AT_name") is not None
80-
# ):
81-
# return # Skipping external variables. YA
82-
# self.var_name = die_variable.attributes.get("DW_AT_name").value.decode(
83-
# "utf-8"
84-
# )
85-
# self.die_variable = die_variable
86-
# self._extract_address(die_variable)
78+
# die_variable.attributes.get("DW_AT_external")
79+
# and die_variable.attributes.get("DW_AT_name") is not None
80+
# ):
81+
# self.var_name = die_variable.attributes.get("DW_AT_name").value.decode(
82+
# "utf-8"
83+
# )
84+
# self.die_variable = die_variable
85+
# self._extract_address(die_variable)
8786
else:
8887
return
8988

@@ -397,7 +396,7 @@ def _get_structure_members_recursive(self, die, parent_name: str, prev_offset=0)
397396
if name_attr:
398397
member_name += "." + name_attr.value.decode("utf-8")
399398
self._process_structure_member(member, child_die, prev_offset, offset, member_name)
400-
members.update(member)
399+
members.update(member) # member should be varinfo?
401400
return members, prev_offset
402401

403402
def _get_structure_members(self, structure_die, var_name):

pyx2cscope/variable/variable.py

Lines changed: 117 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,46 @@
1515
- VariableInt64: Represents a 64-bit signed integer variable.
1616
- VariableUint64: Represents a 64-bit unsigned integer variable.
1717
- VariableFloat: Represents a floating-point number variable.
18+
- VariableEnum: Represents an enum variable.
1819
1920
"""
2021

2122
import logging
2223
import struct
2324
from abc import abstractmethod
25+
from dataclasses import dataclass
2426
from numbers import Number
25-
from typing import List
27+
from typing import List, Dict
2628

27-
from mchplnet import lnet
29+
from mchplnet.lnet import LNet
30+
31+
32+
@dataclass
33+
class VariableInfo:
34+
"""A raw representation about a variable.
35+
36+
Attributes:
37+
name (str): The name of the variable.
38+
type (str): The data type of the variable.
39+
byte_size (int): The size of the variable in bytes.
40+
address (int): The memory address of the variable.
41+
array_size (int): The size of the array if the variable is an array, default is 0.
42+
valid_values (dict): enum type of valid values
43+
"""
44+
45+
name: str
46+
type: str
47+
byte_size: int
48+
address: int
49+
array_size: int
50+
valid_values: Dict[str, int]
2851

2952

3053
class Variable:
3154
"""Represents a variable in the MCU data memory."""
3255

3356
def __init__(
34-
self, l_net: lnet, address: int, array_size: int, name: str = None
57+
self, l_net: LNet, address: int, array_size: int, name: str = None
3558
) -> None:
3659
"""Initialize the Variable object.
3760
@@ -869,3 +892,94 @@ def bytes_to_value(self, data: bytearray) -> Number:
869892
"""
870893
data = bytearray(data)
871894
return struct.unpack("<f", data)[0]
895+
896+
897+
# ------------------------------ Enum 2bytes ------------------------------
898+
class VariableEnum(Variable):
899+
"""Represents enum variable in the MCU data memory."""
900+
901+
def __init__(self, l_net: LNet, address: int, array_size: int, name: str,
902+
enum_list: dict[str, int]):
903+
"""Initialize the Variable object. But needs customised constructor due to enum list initialisation.
904+
905+
Args:
906+
l_net (LNet): LNet protocol that handles the communication with the target device.
907+
address (int): Address of the variable in the MCU memory.
908+
array_size (int): The number of elements in the array, 0 in case of a plain variable.
909+
name (str, optional): Name of the variable. Defaults to None.
910+
enum_list (dict[str, int]): The enumeration list with the values.
911+
"""
912+
super().__init__(l_net, address, array_size, name)
913+
self.enum_list = enum_list
914+
915+
def _get_min_max_values(self) -> tuple[Number, Number]:
916+
"""Get the minimum and maximum values for the 16-bit enum.
917+
918+
Returns:
919+
tuple[Number, Number]: The minimum and maximum values.
920+
"""
921+
return min(self.enum_list.values()), max(self.enum_list.values())
922+
923+
def is_integer(self) -> bool:
924+
"""Check if the variable is an integer.
925+
926+
Returns:
927+
bool: Enumeration is stricktly an integer.
928+
"""
929+
return True
930+
931+
def is_signed(self) -> bool:
932+
"""Check if the variable is signed.
933+
934+
Returns:
935+
bool: Depending on the enum values.
936+
"""
937+
if min(self.enum_list.values()) < 0:
938+
return True
939+
else:
940+
return False
941+
942+
def get_width(self) -> int:
943+
"""Get the width of the 16-bit enum.
944+
945+
Returns:
946+
int: Width of the variable, which is 2.
947+
"""
948+
#TODO depends on architecture and enum count of elements
949+
return self.l_net.device_info.uc_width
950+
951+
952+
def set_value(self, value: int):
953+
"""Set the value of the 16-bit enum.
954+
955+
Args:
956+
value (int): The value to set.
957+
"""
958+
try:
959+
self._check_value_range(value) #TODO might be different than super class
960+
int_value = int(value)
961+
bytes_data = int_value.to_bytes(
962+
length=self.l_net.device_info.uc_width, byteorder="little", signed=self.is_signed()
963+
) # construct the bytes representation of the value
964+
self._set_value_raw(bytes_data)
965+
except Exception as e:
966+
logging.error(e)
967+
968+
def bytes_to_value(self, data: bytearray) -> Number:
969+
"""Convert the byte array to enum.
970+
971+
Args:
972+
data (bytearray): The byte array to convert.
973+
974+
Returns:
975+
Number: The enum value.
976+
"""
977+
return int.from_bytes(data, "little", signed=self.is_signed())
978+
979+
def get_enumerator_list(self) -> Dict[str, int]:
980+
"""Get the valid values for the enum variable.
981+
982+
Returns:
983+
Dict[str, int]: A dictionary of valid values for the enum variable.
984+
"""
985+
return self.enum_list

0 commit comments

Comments
 (0)