交互式执行模型
交互模式为模型的正常启动增加了双向通信通道。 AnyMath 和目标平台上的运行时。 通过这个渠道 AnyMath 接收所选信号的值并发送命令以更改配置的参数,而无需重新组装和加载模型。
在用户目标的当前实现中,此模式是围绕_XCP slave_构建的,它嵌入在目标平台上模型的运行时中。
基本概念
逻辑点是用户在执行过程中选择要监视的模型的信号或输出端口。 生成C代码时 AnyMath 在元数据中存储有关此类点的信息,并生成运行时可以从中读取值的Si数据。 在代码生成的Python表示中,这些数据反映在 代码信息 并以简化的方式为模板 CodeGenInfo。logging_signals.
可配置参数是模型参数,其值可以在启动后更改。 在生成的C代码中,此类参数通常落入模型参数的单独结构中。 [医]代码信息 这部分通过描述 可调参数. 对于交互模式,重要的是相应的C结构在目标应用程序的内存中可用,并且不会被优化器删除。
目标平台接口是一个物理或编程通道,通过该通道 AnyMath 与模型交换数据。 它可以是UART,TCP,UDP,USB CDC,调试通道或其他传输。 模型中存在可记录点这一事实并不会将数据传输到 AnyMath :在目标平台上需要一个运行时通信通道。
ELF文件是一个组装的工件,其中包含目标的Python代码用来查找目标应用程序内存中信号和参数的地址的符号。 对于交互式摘要模式 .精灵 它必须在客户端可用,并且不能从中删除有关必要符号的信息。
XCP协议
Xcp(通用测量和校准协议)是ASAM MCD-1XCP标准,用于在其运行期间访问内部应用数据。 该协议通常用于测量信号,校准参数和调试嵌入式应用程序,而无需在每次参数更改后重新组装和闪烁。
在本指南中 AnyMath 充当_XCP master_,目标平台上模型的运行时包含_XCP slave_。 主站管理会话:连接从站,配置信号传输,发送读写命令。 从站驻留在目标应用程序内部,执行主命令,并读取或修改模型的内存。
该标准的官方描述可在链接中找到 ASAM MCD-1XCP。
XCP层:协议和传输
XCP将逻辑协议和传输层分开。
协议层描述了存在哪些命令以及它们的含义:连接,读取和写入内存,配置DAQ,启动和停止传输。 此级别不依赖于交换是通过UART、TCP、UDP、CAN、USB还是其他通道。
传输层描述了XCP数据包如何通过特定接口传输:如何打开连接,如何突出显示数据包边界,最大帧大小是多少,是否有确认,如何处理错误和超时。
这意味着您需要定义:
-
哪个XCP从站将嵌入到模型的运行时中。
-
什么样的交通工具 AnyMath 将与这个奴隶交换。
作为可执行文件的一部分,XCP slave的协议部分已经随目标基础结构一起提供。 对于新平台,您只需要选择或实现传输和平台层。
主、从和通信会话
XCP会话从主机到从机的连接开始。 连接后,主机将获得从机的基本功能:支持的传输、数据包大小、可用资源和限制。 然后,主控器可以执行命令。
对于交互模式 AnyMath 三组团队很重要:
*会话服务命令:连接、检查状态、完成交换; *内存访问命令:读取信号值或写入新的参数值; *DAQ命令:设置测量数据列表并开始定期传输。
在经典的XCP中,从站事先不知道用户想要观察哪些信号。 它提供对内存的通用访问,并且主机调整需要读取的地址和数据大小。 在 AnyMath 这些地址取自 .精灵 和代码生成元数据。
数据寻址
XCP通过内存地址处理目标应用程序对象。 为 AnyMath 这些对象是:
*记录信号的值; *可配置模型参数; *交互式执行模式运行时所需的服务结构。
主机看到的地址必须正确转换为从机内部的指针。 平台宏对此负责。 XCP_地址_GET. 在没有虚拟存储器的微控制器上,地址通常可以直接使用。 在具有PIE/ASLR地址的主机平台上 .精灵 可能存在偏移,因此平台层必须考虑到上传图像的基地址。
因此,交互式执行模式很重要 .精灵 带符号:目标的Python代码必须找到数据地址,XCP从站必须能够使用这些地址访问目标应用程序的内存。
DAQ、ODT和事件
DAQ(数据采集)是一种XCP机制,用于将数据从从机流式传输到主机。 主站预先配置正在发送的数据的组成,从站在事件发生时进行传输。
DAQ设置由几个级别组成: *DAQ列表-由事件激活的数据列表; *ODT(对象描述符表—-适合单个DTO包的一组元素; *ODT条目-一个可测量的对象:地址、大小和附加属性; *事件通道-从站必须收集和发送数据的运行时事件。
在模型中 AnyMath 这样的事件通常是一个模型步骤的完成。 运行时原因 rtExtModeUpload,并由XCP从机发送数据。
配置XCP时,重要的是要考虑尺寸:
*用户可以在同一时间记录多少信号; *一个DTO数据包可以容纳多少字节; *一个ODT中允许有多少个ODT记录; *平台上有多少DAQ列表可用; *DAQ数据包中是否需要时间戳以及需要多少字节。
如果限制太小,一些信号将无法包含在流中。 如果限制太大,XCP从机将占用更多RAM,这对于小型Mcu至关重要。
记录参数
以交互方式更改参数是将新值写入目标应用程序的内存。 主控选择参数地址并发送记录数据。 从站检查命令并将字节写入内存。
为了正确的操作,有必要:
*参数实际上在内存中,并没有被优化器删除; *与代码生成信息匹配的数据大小和类型; *写入与从另一个线程或中断处理程序(ISR)进行的不安全读取不同时发生; *模型的运行时已准备好在接下来的步骤中使用新值。
如果参数是模型结构的一部分,则更改通常从模型下次访问此结构时开始影响计算。 如果在初始化期间将值复制到本地状态,则对源参数的简单XCP写入可能没有预期的效果。
目标配置
要配置XCP,您需要定义:
-
传输:UART,TCP,UDP或其他通道。
-
DAQ限制:列表、Odt和ODT元素的数量。
-
将XCP地址转换为指针的方法。
-
同步模型:是否需要互斥锁,其中线程或中断处理程序(线程/ISR)是可能的。
一般数据流
交互式执行模式作为一个由几个部分组成的链:
-
AnyMath 生成模型的C代码和有关信号、参数和入口点的元数据。
-
目标生成交互模式的运行时模板。
-
XCP从站和平台传输正在添加到项目中。
-
工具链收集工件和
.精灵与模型的符号。 -
Target的Python代码解析
.精灵并获取记录点和参数的地址。 -
AnyMath 通过所选接口连接到XCP从站。
-
模型的调度程序执行step函数并调用DAQ事件。
-
XCP从站从内存中读取值并将它们传递给 AnyMath .
-
更改参数时 AnyMath 发送写命令,XCP从站将新值写入模型的内存。
Python-目标代码
交互模式目标通常继承自 基本目标 和 XCPTarget的. 基本目标 设置管理模型操作的方法,以及 XCPTarget的 提供Python交换代码:配置端点、创建流队列、启动数据读取和更改参数。
基本继承的一个例子
from targets.base_target import BaseTarget
from targets.xcp_target.xcp_target import XCPTarget
class MyTarget(BaseTarget, XCPTarget):
def __init__(self) -> None:
XCPTarget.__init__(self)
...
在 generate_executable_code 我们需要准备一个交互模式草案。:
检查 model_settings.is_ext_mode;
*选择具有调用的运行时模板 rtExtMode,rtExtMode;
*保存模型的源代码;
*通过添加共享XCP从源 get_xcp_slave_src_list;
*添加平台传输文件;
*为头文件和编译定义添加必要的路径;
*提供装配 .精灵 用符号。
在 start_model的 我们需要准备一个连接 AnyMath 已组装并运行的模型:
-
通过以下方式设置端点
XCPTarget的。set_endpoint. -
找到最终结果
.精灵. -
通过以下方式获取有关信号和参数的数据
get_engee_elf_info. -
打电话
XCPTarget的。setup_流. -
回来吧
目标反应与数据_stream_q,controll_stream_q和发现_信号.start_stream通常原因XCPTarget的。_start_stream,change_param原因set_param,而停止模型停止流过XCPTarget的。停止流.
目标的Python代码的方法方案的一个例子
from targets.xcp_target.engee_elf_info import get_engee_elf_info
from targets.xcp_target.xcp_target import get_xcp_slave_src_list
async def start_model(self, model: EngeeModel) -> TargetResponse:
XCPTarget.set_endpoint(self, interface="uart", url=self.target_block.com_port)
elf_data = get_engee_elf_info(
elf_path=self.elf_path,
code_info=self.code_info,
)
data_q,control_q,discovered=await XCPTarget。setup_streaming(
自我,
elf_数据,
自我。model_settings,
)
返回目标响应(
细节="start_model",
data_stream_q=data_q,
control_stream_q=control_q,
discovered_signals=发现,
)
async def start_stream(self,simulation_uuid:str)->结果:
等待XCPTarget。_start_stream(self,simulation_uuid)
返回结果。成功()
async def change_param(self,block_key,param,data)->TargetResponse:
等待自我。set_param(block_key,param,data)
返回TargetResponse(detail="Param已更改")
async def stop_model(self)->TargetResponse:
等待XCPTarget。stop_stream(self)
返回TargetResponse(detail="stop_model")
运行时-交互式执行模式模板
交互式执行模式运行时构建在独立模式调度程序之上,但将API调用添加到交互式模式适配器(external-mode adapter ext_work。h.
最低订购量:
-
初始化模型。
-
打电话
[医][医][医][医][医][医]. -
等待启动命令通过
rtExtModeWaitForStartPkt. -
在循环中执行模型的阶跃函数。
-
呼叫步骤后
rtExtModeUpload发布DAQ事件。 -
在每一步,调用
rtExtModeOneStep来处理主命令。 -
请求停止时,呼叫
终止;终止. -
正常完成后,致电
rtExtModeShutdown如果平台支持正确关闭(干净关闭)。
重要的是 rtExtModeOneStep 他经常被叫来. 如果调度程序在驱动程序中阻塞了很长时间,同时等待外设或睡眠功能, AnyMath 它将接收具有延迟的参数更改的数据和命令。
运行时循环方案的示例
#include "ext_work.h"
RTWExtModeInfo ext_mode_info;
boolean_T stop_requested = false;
model_initialize();
rtExtModeCheckInit(1);
rtExtModeWaitForStartPkt(&ext;_mode_info, 1, &stop;_requested);
rtERTExtModeStartMsg();
while (!stop_requested) {
run_due_model_steps();
rtExtModeUpload(0, getCurrentTimestamp());
rtExtModeOneStep(&ext;_mode_info, 1, &stop;_requested);
wait_until_next_tick();
}
model_terminate();
rtExtModeShutdown(1);
在Engee的xcp奴隶。综合服务
共享的XCP从站包含在提供的可执行文件中。 工程师-设备经理. 目标的Python代码通过以下方式将其添加到项目中 get_xcp_slave_src_list,并分别添加平台层。
XCP从属结构:
-
包括/ext_work。h-调用模型运行时模板的API; -
src/ext_work。c-之间的适配器rtExtMode,rtExtMode*和XCP奴隶; -
src/xcp_daq.c,src/xcp_daq.h—XCP命令、DAQ列表、读取信号和写入参数的处理; -
src/xcp_frame/xcp_frame_uart。c-uart框架; -
src/xcp_frame/xcp_frame_tcp。c-tcp的成帧; -
src/xcp_frame/xcp_frame_udp。c-UDP成帧; -
包括/xcp_config。h-XCP slave在编译阶段的配置; -
包括/rtiostream。h-传输I/O接口。
主要功能 rtExtMode,rtExtMode*:
-
[医][医][医][医][医][医]-初始化xcp从站并打开传输; -
rtExtModeWaitForStartPkt-等待主人的启动命令; -
rtExtModeUpload-发布DAQ事件; -
rtExtModeOneStep-执行一个非阻塞命令处理通行证; -
需要的;需要的-在暂停状态下提供命令; -
rtExtModeShutdown-重置从状态并关闭运行时; -
[医][医][医][医][医][医]和rtERTExtModeStartMsg的左为与生成的交互模式API(外部模式)的兼容性。
Xcp的编译定义
必须在项目中选择运输。:
-DXCP_UART_TRANSPORT
或
-DXCP_TCP_TRANSPORT
或
-DXCP_UDP_TRANSPORT
对于嵌入式平台,他们通常会添加:
-DXCP_CUSTOM_PLATFORM
您可以为x86主机平台使用现成的平台层。:
-DXCP_PLATFORM_X86
xcp_config。h 它还包含可定制的尺寸。:
-
XCP_MAX_ODT_ENTRIES_COUNT-一个ODT中的最大元素; -
XCP_MAX_DAQ_列表-DAQ列表的最大值; -
XCP_DAQ_TIMESTAMP_SIZE-DAQ包中时间戳的大小; -
XCP_DAQ_DATA_SIZE-一个DTO中最大有用的DAQ数据; -
XCP_MAX_SHORT_DOWNLOAD_DATA_LEN-使用单个命令记录参数的最大数据; -
XCP_MAX_CTO_LEN-CTO命令/响应包的大小; -
XCP_MAX_DTO_LEN-dto数据传输包的大小。
对于小型Mcu,可以减少这些值,但之后您需要检查所选日志点是否符合DAQ限制。
用户平台
如果xcp从属构建未在现成的x86层上运行,则需要启用 -DXCP_CUSTOM_PLATFORM 并提供 xcp_platform_custom。h.
最低平台要求集包括以下宏和指令的实现:
-
XCP_PRINTF-诊断输出或空宏; -
XCP_MUTEX_DEFINE; -
XCP_MUTEX_INIT; -
XCP_MUTEX_LOCK; -
XCP_MUTEX_UNLOCK; -
XCP_地址_GET; -
XCP_睡眠; *包装/对齐宏:XCP_PRAGMA_PACK_BEGIN,XCP_PRAGMA_PACK_END,XCP_ATTRIBUTE_ALIGNED,XCP_ATTRIBUTE_打包.
基本示例 xcp_platform_custom。h
#ifndef XCP_PLATFORM_CUSTOM_H
#define XCP_PLATFORM_CUSTOM_H
#include <stdint.h>
#include "rtwtypes.h"
#define XCP_PRINTF(...)
#define XCP_MUTEX_DEFINE(lock)
#define XCP_MUTEX_INIT(lock)
#define XCP_MUTEX_LOCK(lock)
#define XCP_MUTEX_UNLOCK(lock)
#define XCP_ADDRESS_GET(addressExtension, address) \
((uint8_T *)((uintptr_t)(address)))
#define XCP_SLEEP(seconds, microseconds) platform_sleep(seconds, microseconds)
#define PRAGMA(n) _Pragma(#n)
#define XCP_PRAGMA_PACK_BEGIN(n) PRAGMA(pack(push, n))
#define XCP_PRAGMA_PACK_END() PRAGMA(pack(pop))
#define XCP_ATTRIBUTE_ALIGNED(n)
#define XCP_ATTRIBUTE_PACKED
#endif
XCP_地址_GET -关键任务宏。 它将xcp命令中的地址转换为目标应用程序地址空间中的真实指针。
对于没有虚拟内存的微控制器,直接地址到指针转换通常就足够了。 对于PIE/ASLR主机应用程序,来自ELF文件的地址可以设置为偏移量,并且必须将下载的映像的基地址添加到其中。 这已在 ritm-xcpslave/平台_x86.
互斥锁取决于执行模型:
*如果仅从一个主循环调用整个XCP从站,则关键部分可能为空。; *如果I/O、DAQ或调度程序从不同的任务、线程或中断处理程序运行,则需要真正的锁。; *阻塞不应永久暂停模型基本步骤的执行。
海关运输
传输必须实现API [医]流星雨:
int rtIOStreamOpen(int argc, void *argv[]);
int rtIOStreamSend(int streamID, const void *src, size_t size, size_t *sizeSent);
int rtIOStreamRecv(int streamID, void *dst, size_t size, size_t *sizeRecvd);
int rtIOStreamClose(int streamID);
规则 [医][医][医][医]:
*返回 RTIOSTREAM_NO_ERROR 如果运输工作正常;
*如果您现在无法发送数据,请安装 *sizeSent=0
*如果所选成帧层不支持部分发送,请勿确认部分帧发送。;
*在真正的运输错误的情况下,返回 RTIOSTREAM_ERROR.
规则 [医][医][医][医]:
*该功能必须是非阻塞的或具有较短的时间限制。;
*如果没有数据,请安装 *sizeRecvd=0 还回去 RTIOSTREAM_NO_ERROR;
*返回的字节比请求的少:帧解析器将通过以下调用完成数据包的读取;
*在传输错误的情况下,返回 RTIOSTREAM_ERROR.
对于UART,当前帧格式使用起始字节。 0xFF,有效载荷的长度,数据本身和XOR CRC。 UART传输必须在没有回声、文本处理或行尾字符转换的情况下传输字节.
复制到项目的内容
对于交互式执行项目,您需要:
-
ritm-xcpslave/include/*。h; -
ritm-xcpslave/src/ext_work。c; -
ritm-xcpslave/src/xcp_daq。c; -
ritm-xcpslave/src/xcp_daq。h; -
ritm-xcpslave/src/xcp_frame/xcp_frame。h; *一个帧处理程序文件:xcp_frame_uart。c,xcp_frame_tcp。c或xcp_frame_udp。c; *基于平台的xcp_platform_custom。h或现成的平台_x86; *实施[医][医][医][医][医],[医][医][医],[医][医][医],[医][医][医][医][医]; *如有必要,您自己的rtwtypes。h和ext_mode_types。h.
精灵和符号
用于交互式执行 AnyMath 必须将记录的点和模型参数与目标应用程序内存中的地址相匹配。 为此,目标的Python代码使用 .精灵 和 get_engee_elf_info.
在实践中,有必要确保以下内容:
*决赛 .精灵 必须在客户端可用;
*信号和参数符号不应被删除 条带;
*优化器不应删除日志记录和更改参数所需的结构。;
*结构层必须与来自 代码信息;
*对于非标准架构,您需要检查ELF解析,指针大小和寻址规则。
清单
-
模型具有逻辑点和至少一个验证信号。
-
XCP slave已添加到项目中。
-
选择一种传输方式:UART、TCP或UDP。
-
传输和平台的编译定义。
-
实施或选择
xcp_platform_custom。h. -
已实施
[医]流星雨为所选择的接口。 -
运行时模板导致
[医][医][医][医][医][医],rtExtModeWaitForStartPkt,rtExtModeUpload,rtExtModeOneStep和rtExtModeShutdown. -
.精灵它包含必要的字符,并且可用于Python目标。 -
XCPTarget的。setup_流返回队列和找到的信号列表。 -
在演示模型上测试了启动、信号读取、参数变化和停止.