参考
Zynq上UART/IIC/SPI的27个实验-第24课:PL 逻辑模拟 SPI 从设备
用于连接一些spi外设
json
{
"remark": "smp.j2b.json5;MCU 与 FPGA 之间的 SPI 半双工请求-响应协议",
"agreement": [
"1. 物理四线 SPI:SCLK/MOSI/MISO/CS,Mode0(CPOL=0,CPHA=0),MSB First",
"2. 逻辑半双工:MCU 先发请求帧(FPGA 只收),请求发完后 MCU 继续发时钟但 MOSI 发 dummy,FPGA 此时才把响应帧放 MISO",
"3. 一次事务 CS 全程拉低,事务结束 CS 拉高",
"4. 请求帧 SOF=0xA5,响应帧 SOF=0x5A,用于区分方向",
"5. 所有多字节字段大端(网络序),对应 j2b 的 U16/U32",
"6. 校验方式:全异或,覆盖 [cmd, len, payload],不含 SOF 和 CHECK 自己",
"7. 帧间 CS 拉高,FPGA 状态机复位回 IDLE",
"8. MISO 在 FPGA 非发送阶段置高阻",
"9. 每个 content 对应一个 cmd,请求和响应成对",
"10. 帧容量 1024 字节,实际长度由 len 字段决定",
"11. 请求帧总长 = 1 + 1 + 2 + len + 1 = len + 5",
"12. 响应帧总长 = 1 + 1 + 2 + len + 1 = len + 5",
"13. payload 直接是 u8[len],靠 len 界定边界",
"14. len 可以为 0,表示 payload 为空;此时帧总长 = 5 字节",
"15. len = 0 仍是一个完整响应帧,不等于不响应;所有请求默认都必须响应",
"16. status 是链路层状态,result 是业务层状态,两者复用同一套 StatusEnum"
],
"schema": {
"StatusEnum:u8;状态码": {
"0x00": "OK;成功",
"0xE1": "ERR_CHECK;异或校验失败",
"0xE2": "ERR_CMD;未知命令字",
"0xE3": "ERR_ADDR;地址越界",
"0xE4": "ERR_LEN;长度非法",
"0xE5": "ERR_BUSY;FPGA 忙"
},
"CmdEnum:u8;命令字": {
"0x01": "RD_REG;读单个寄存器",
"0x02": "WR_REG;写单个寄存器",
"0x03": "RD_BLOCK;读一块数据",
"0x04": "WR_BLOCK;写一块数据",
"0x05": "STATUS;读状态字",
"0x06": "RESET;复位 FPGA"
},
"Frame:u8[?]:帧格式": {
"ReqFrame;请求帧;SOF=0xA5": {
"sof": "0xA5:u8;帧头固定0xA5,方向 MCU->FPGA",
"cmd": "CmdEnum:u8;请求命令字",
"len": ":U16;PAYLOAD 字节数",
"payload": "u8[len];命令相关字段,见各 content.req",
"check": "u8;全异或校验,覆盖 [cmd,len,payload],不含 sof 和 check 自己",
"总长": "len + 5 字节"
},
"ResFrame;响应帧;SOF=0x5A": {
"sof": "0x5A:u8;帧头固定0x5A,方向 FPGA->MCU",
"status": "StatusEnum:u8;链路层状态",
"len": ":U16;PAYLOAD 字节数",
"payload": "u8[len];响应的数据内容value,result,data等",
"check": "u8;全异或校验,覆盖 [status,len,payload],不含 sof 和 check 自己",
"总长": "len + 5 字节"
}
},
"ReadReg:Frame;读单个寄存器": {
"req": {
"sof": "0xA5:u8",
"cmd": "0x01:CmdEnum:u8;请求命令字",
"len": "0x0002:U16;PAYLOAD 字节数",
"addr": "0:U16;寄存器地址,大端",
"check": "0:u8;全异或校验,覆盖 [cmd,len,addr]"
},
"res": {
"sof": "0x5A:u8",
"status": "0x00:StatusEnum;链路层状态",
"len": "0x0004:U16;PAYLOAD 字节数",
"value": "0:U32;寄存器值,大端",
"check": "0:u8;全异或校验,覆盖 [status,len,value]"
}
},
"WriteReg:Frame;写单个寄存器;cmd=0x02;": {
"req": {
"sof": "0xA5:u8",
"cmd": "0x02:CmdEnum:u8;请求命令字",
"len": "0x0006:U16;PAYLOAD 字节数",
"addr": "0:U16;寄存器地址",
"value": "0:U32;写入值",
"check": "0:u8;全异或校验,覆盖 [cmd,len,addr,value]"
},
"res": {
"sof": "0x5A:u8",
"status": "0x00:StatusEnum;链路层状态",
"len": "0x0001:U16;PAYLOAD 字节数",
"result": "0x00:StatusEnum;业务层结果",
"check": "0:u8;全异或校验,覆盖 [status,len,result]"
}
},
"ReadBlock:Frame;读一块数据;cmd=0x03": {
"req": {
"sof": "0xA5:u8",
"cmd": "0x03:CmdEnum:u8;请求命令字",
"len": "0x0004:U16;PAYLOAD 字节数",
"addr": "0:U16;起始地址",
"length": "0:U16;读取字节数",
"check": "0:u8;全异或校验,覆盖 [cmd,len,addr,length]"
},
"res": {
"sof": "0x5A:u8",
"status": "0x00:StatusEnum;链路层状态",
"len": ":U16;PAYLOAD 字节数",
"data": "0:u8[length];读取到的数据,长度=length",
"check": "0:u8;全异或校验,覆盖 [status,len,data]"
}
},
"WriteBlock:Frame;写一块数据;cmd=0x04": {
"req": {
"sof": "0xA5:u8",
"cmd": "0x04:CmdEnum:u8;",
"len": ":U16;字节数",
"addr": "0:U16;起始地址",
"length": "0:U16;写入字节数",
"data": "0:u8[length];写入数据",
"check": "0:u8;全异或校验,覆盖 [cmd,len,addr,length,data]"
},
"res": {
"sof": "0x5A:u8",
"status": "0x00:StatusEnum:u8;链路层状态",
"len": "0x0001:U16;PAYLOAD 字节数",
"result": "0x00:StatusEnum;业务层结果",
"check": "0:u8;全异或校验,覆盖 [status,len,result]"
}
},
"GetStatus:Frame;读状态字;cmd=0x05": {
"req": {
"sof": "0xA5:u8",
"cmd": "0x05:CmdEnum:u8",
"len": "0x0000:U16;无 payload",
"check": "0:u8;全异或校验,覆盖 [cmd,len]"
},
"res": {
"sof": "0x5A:u8",
"status": "0x00:StatusEnum:u8;链路层状态",
"len": "0x0004:U16;PAYLOAD 字节数",
"statusWord": "0:U32;FPGA 状态字,大端",
"check": "0:u8;全异或校验,覆盖 [status,len,statusWord]"
}
},
"Reset:Frame;复位 FPGA;cmd=0x06": {
"req": {
"sof": "0xA5:u8;",
"cmd": "0x06:CmdEnum:u8",
"len": "0x0001:U16;PAYLOAD 字节数",
"magic": "0x5A:u8;复位魔数,防止误触发",
"check": "0:u8;全异或校验,覆盖 [cmd,len,magic]"
},
"res": {
"sof": "0x5A:u8",
"status": "0x00:StatusEnum;链路层状态",
"len": "0x0001:U16;PAYLOAD 字节数",
"result": "0x00:StatusEnum;业务层结果",
"check": "0:u8;全异或校验,覆盖 [status,len,result]"
}
}
},
"demoContent;示例": {
"readReg": {
"req": "A5 01 00 02 00 10 13",
"res": "5A 00 00 04 DE AD BE EF 26"
},
"writeReg": {
"req": "A5 02 00 06 00 10 DE AD BE EF 36",
"res": "5A 00 00 01 00 01"
},
"readBlock": {
"req": "A5 03 00 04 00 10 00 04 13",
"res": "5A 00 00 04 11 22 33 44 40"
},
"writeBlock": {
"req": "A5 04 00 08 00 10 00 04 11 22 33 44 5C",
"res": "5A 00 00 01 00 01"
},
"getStatus": {
"req": "A5 05 00 00 05",
"res": "5A 00 00 04 00 00 00 01 05"
},
"reset": {
"req": "A5 06 00 01 5A 5D",
"res": "5A 00 00 01 00 01"
}
}
}