Добавить протокол Altera Logic и общие клиенты прошивки

This commit is contained in:
2026-09-19 07:12:09 +03:00
parent def3eb08f3
commit 80ba17d77d
38 changed files with 3271 additions and 10 deletions

View File

@@ -0,0 +1,142 @@
#include "altera_logic.h"
#include <string.h>
typedef struct {
uint32_t state, error, clock_hz, elapsed, delay;
uint16_t config[5], channels, depth, trigger, count, offset;
uint16_t samples[4096];
uint8_t command, pending, flags, requested, rx[133];
size_t used;
} la_context;
static uint8_t checksum(const uint8_t *p, size_t n) {
uint8_t x = 0; size_t i;
for (i = 0; i < n; ++i) x ^= p[i];
return x;
}
static uint16_t u16(const uint8_t *p) {
return (uint16_t)((uint16_t)p[0] | ((uint16_t)p[1] << 8));
}
size_t la_context_size(void) { return sizeof(la_context); }
void la_init(void *ctx) {
la_context *s = (la_context *)ctx;
memset(s, 0, sizeof(*s)); s->command = 1; s->state = LA_CONNECTING;
}
void la_fail(void *ctx, uint32_t code) {
la_context *s = (la_context *)ctx;
s->state = LA_ERROR; s->error = code; s->pending = 0; s->command = 0;
}
int la_start(void *ctx, uint32_t div, uint32_t mask, uint32_t val,
uint32_t emask, uint32_t eval) {
la_context *s = (la_context *)ctx;
if ((s->state != LA_READY && s->state != LA_DONE) ||
div > 65535 || mask > 65535 || val > 65535 ||
emask > 65535 || eval > 65535 || ((val ^ eval) & mask & emask))
return LA_ARGUMENT;
s->config[0]=(uint16_t)div; s->config[1]=(uint16_t)mask;
s->config[2]=(uint16_t)val; s->config[3]=(uint16_t)emask;
s->config[4]=(uint16_t)eval;
s->state=LA_CAPTURING; s->error=0; s->flags=0; s->count=0;
s->offset=0; s->command=2; s->delay=0;
return LA_OK;
}
size_t la_next(void *ctx, uint8_t *out, size_t capacity) {
la_context *s = (la_context *)ctx; uint16_t arg = 0;
if (!out || capacity < 6 || !s->command || s->pending || s->delay) return 0;
if (s->command >= 2 && s->command <= 6) arg=s->config[s->command-2];
if (s->command == 9) {
arg=s->offset;
s->requested=(uint8_t)((s->depth-s->offset > 64) ? 64 : s->depth-s->offset);
}
out[0]=0xa5; out[1]=s->command; out[2]=(uint8_t)arg;
out[3]=(uint8_t)(arg >> 8); out[4]=(s->command == 9) ? s->requested : 0;
out[5]=checksum(out,5);
s->pending=1; s->elapsed=0; s->used=0;
return 6;
}
static void complete(la_context *s) {
size_t i;
if (checksum(s->rx,s->used)) { la_fail(s,LA_CHECKSUM); return; }
if (s->rx[2]) { la_fail(s,LA_DEVICE_ERROR); return; }
s->pending=0;
if (s->command == 1) {
s->channels=s->rx[3]; s->depth=u16(s->rx+4);
s->clock_hz=(uint32_t)s->rx[6]*1000000U;
if (s->channels != 16 || s->depth != 4096 || !s->clock_hz || s->rx[7]!=1) {
la_fail(s,LA_UNSUPPORTED); return;
}
s->state=LA_READY; s->command=0;
} else if (s->command < 7) ++s->command;
else if (s->command == 7) { s->command=8; s->delay=25; }
else if (s->command == 8) {
s->flags=s->rx[3]; s->trigger=u16(s->rx+4);
if (s->flags & 4) {
if (s->trigger >= s->depth || (s->flags & 1)) { la_fail(s,LA_BAD_FRAME); return; }
s->command=9;
} else s->delay=25;
} else {
for(i=0; i<s->requested; ++i) s->samples[s->offset+i]=u16(s->rx+4+i*2);
s->offset=(uint16_t)(s->offset+s->requested); s->count=s->offset;
if(s->offset == s->depth) { s->state=LA_DONE; s->command=0; }
}
}
void la_feed(void *ctx, const uint8_t *data, size_t size) {
la_context *s = (la_context *)ctx; size_t i, expected;
if (!data) return;
for(i=0; i<size; ++i) {
if(!s->pending || s->used >= sizeof(s->rx)) { la_fail(s,LA_BAD_FRAME); return; }
s->rx[s->used++]=data[i];
if(s->rx[0]!=0x5a || (s->used>=2 && s->rx[1]!=(s->command|0x80))) {
la_fail(s,LA_BAD_FRAME); return;
}
if(s->used<3) continue;
expected=4;
if(!s->rx[2]) {
if(s->command==1) expected=9;
else if(s->command==8) expected=7;
else if(s->command==9) {
if(s->used<4) continue;
if(s->rx[3]!=s->requested) { la_fail(s,LA_BAD_FRAME); return; }
expected=5U+2U*s->requested;
}
}
if(s->used==expected) complete(s);
}
}
void la_tick(void *ctx, uint32_t ms) {
la_context *s=(la_context *)ctx;
if(s->pending) {
if(ms >= 1000U-s->elapsed) la_fail(s,LA_TIMEOUT);
else s->elapsed+=ms;
}
s->delay=(ms >= s->delay) ? 0 : s->delay-ms;
}
uint32_t la_get(const void *ctx, uint32_t field) {
const la_context *s=(const la_context *)ctx;
switch(field) {
case LA_STATE:return s->state; case LA_ERROR_CODE:return s->error;
case LA_CHANNELS:return s->channels; case LA_DEPTH:return s->depth;
case LA_CLOCK_HZ:return s->clock_hz; case LA_FLAGS:return s->flags;
case LA_TRIGGER_INDEX:return s->trigger; case LA_READ_COUNT:return s->count;
case LA_DIVIDER:return s->config[0]; default:return 0;
}
}
size_t la_samples(const void *ctx, uint16_t *out, size_t capacity) {
const la_context *s=(const la_context *)ctx;
if(s->state!=LA_DONE || !out || capacity<s->count) return 0;
memcpy(out,s->samples,s->count*sizeof(uint16_t)); return s->count;
}
void la_demo_init(void *ctx) {
la_context *s=(la_context *)ctx; la_init(s);
s->channels=16; s->depth=4096; s->clock_hz=50000000;
s->state=LA_READY; s->command=0;
}
int la_demo_capture(void *ctx, uint32_t divider) {
la_context *s=(la_context *)ctx; size_t i, bit;
int rc=la_start(s,divider,0,0,0,0); if(rc) return rc;
for(i=0;i<4096;++i) {
uint16_t v=0;
for(bit=0;bit<16;++bit) if(((i/(4U+bit*7U))&1U)!=0) v|=(uint16_t)(1U<<bit);
s->samples[i]=v;
}
s->trigger=2048; s->flags=6; s->count=4096; s->command=0; s->state=LA_DONE;
return LA_OK;
}

View File

@@ -0,0 +1,158 @@
#include "altera_stream.h"
#include "pcan_id.h"
#include <string.h>
typedef struct {
pcan_parser_t parser;
uint64_t indices[LAS_CAPACITY], next_index;
uint16_t samples[LAS_CAPACITY];
uint8_t breaks[LAS_CAPACITY];
uint32_t head, count, period, session, received, missing, duplicates, invalid, ignored;
uint8_t device, metadata, started;
} las_context;
static uint16_t r16(const uint8_t *p) { return (uint16_t)(p[0]|((uint16_t)p[1]<<8)); }
static uint32_t r32(const uint8_t *p) { return (uint32_t)r16(p)|((uint32_t)r16(p+2)<<16); }
static void w16(uint8_t *p,uint16_t v) { p[0]=(uint8_t)v;p[1]=(uint8_t)(v>>8); }
static void w32(uint8_t *p,uint32_t v) { w16(p,(uint16_t)v);w16(p+2,(uint16_t)(v>>16)); }
uint32_t las_device_type(void) { return LAS_DEVICE_TYPE; }
uint32_t las_device_id(void) { return LAS_DEVICE_ID; }
const char *las_device_name(void) { return "Altera Logic"; }
size_t las_context_size(void) { return sizeof(las_context); }
int las_init(void *ctx,uint32_t device) {
las_context *s=(las_context *)ctx;
if(!ctx || device>15) return 0;
memset(s,0,sizeof(*s));s->device=(uint8_t)device;pcan_parser_init(&s->parser);return 1;
}
static void append(las_context *s,uint64_t index,uint16_t value,uint8_t gap) {
s->indices[s->head]=index;s->samples[s->head]=value;s->breaks[s->head]=gap;
s->head=(s->head+1U)%LAS_CAPACITY;
if(s->count<LAS_CAPACITY) ++s->count;
}
void las_can(void *ctx,uint32_t raw,const uint8_t *p,size_t n,uint32_t extended,uint32_t remote) {
las_context *s=(las_context *)ctx;pcan_id_t id;uint32_t index,delta,period,session;
if(!extended || remote || raw>PCAN_ID_MASK) { ++s->ignored;return; }
pcan_id_unpack(raw,&id);
if(id.device_type!=LAS_DEVICE_TYPE || id.device_id!=s->device ||
id.route!=PCAN_ROUTE_FROM_DEVICE || id.msg_type!=PCAN_MSG_GAS ||
(id.msg_body!=LAS_GAS_META && id.msg_body!=LAS_GAS_DATA)) { ++s->ignored;return; }
if(n!=8 || !p) { ++s->invalid;return; }
if(id.msg_body==LAS_GAS_META) {
period=r32(p+4);session=r16(p+2);
if(r16(p)!=LAS_TAG || period==0) { ++s->invalid;return; }
if(s->metadata && session==s->session) {
if(period!=s->period) ++s->invalid;
return;
}
if(s->metadata && (uint16_t)(session-s->session)>=0x8000U) {
++s->duplicates;return;
}
s->metadata=1;s->session=session;s->period=period;
s->count=0;s->head=0;s->started=0;s->next_index=0;
return;
}
if(!s->metadata) { ++s->ignored;return; }
if(r16(p)!=s->session) { ++s->ignored;return; }
index=r32(p+4);
delta=0;
if(s->started) {
delta=index-(uint32_t)s->next_index;
if(delta>=0x80000000U) { ++s->duplicates;return; }
s->missing+=delta;
} else s->next_index=index;
s->next_index+=delta;
append(s,s->next_index,r16(p+2),(uint8_t)(!s->started || delta!=0));
s->next_index+=1;s->started=1;++s->received;
}
static void frame_callback(const pcan_frame_t *frame,void *user) {
if(frame->flags & (PCAN_FLAG_DIR|PCAN_FLAG_ERR)) {
++((las_context *)user)->ignored;return;
}
las_can(user,frame->id,frame->data,frame->dlc,
frame->flags&PCAN_FLAG_IDE,frame->flags&PCAN_FLAG_RTR);
}
void las_uart(void *ctx,const uint8_t *data,size_t size) {
las_context *s=(las_context *)ctx;
if(data) pcan_parser_feed(&s->parser,data,size,frame_callback,s);
}
uint32_t las_get(const void *ctx,uint32_t field) {
const las_context *s=(const las_context *)ctx;
switch(field) {
case LAS_COUNT:return s->count;case LAS_PERIOD_NS:return s->period;
case LAS_SESSION:return s->session;case LAS_RECEIVED:return s->received;
case LAS_MISSING:return s->missing;case LAS_DUPLICATES:return s->duplicates;
case LAS_INVALID:return s->invalid;case LAS_IGNORED:return s->ignored;
case LAS_CRC_ERRORS:return s->parser.stats.crc_errors;
case LAS_HAS_META:return s->metadata;default:return 0;
}
}
size_t las_snapshot(const void *ctx,uint64_t *indices,uint16_t *samples,uint8_t *breaks,size_t cap) {
const las_context *s=(const las_context *)ctx;uint32_t i,pos;
if(!indices || !samples || !breaks || cap<s->count) return 0;
pos=(s->head+LAS_CAPACITY-s->count)%LAS_CAPACITY;
for(i=0;i<s->count;++i) {
indices[i]=s->indices[pos];samples[i]=s->samples[pos];breaks[i]=s->breaks[pos];
pos=(pos+1U)%LAS_CAPACITY;
}
return s->count;
}
size_t las_trace(const void *ctx,uint32_t channel,uint64_t *indices,uint8_t *levels,
uint8_t *moves,size_t cap) {
const las_context *s=(const las_context *)ctx;uint32_t i,pos;
uint64_t previous_x=0;uint8_t previous_y=0;size_t n=0;
if(channel>=16 || !indices || !levels || !moves || cap<2U*s->count) return 0;
pos=(s->head+LAS_CAPACITY-s->count)%LAS_CAPACITY;
for(i=0;i<s->count;++i) {
uint64_t x=s->indices[pos];uint8_t y=(uint8_t)((s->samples[pos]>>channel)&1U);
if(!i) { indices[n]=x;levels[n]=y;moves[n++]=1; }
else if(s->breaks[pos]) {
indices[n]=previous_x;levels[n]=previous_y;moves[n++]=0;
indices[n]=x;levels[n]=y;moves[n++]=1;
} else if(y!=previous_y) {
indices[n]=x;levels[n]=previous_y;moves[n++]=0;
indices[n]=x;levels[n]=y;moves[n++]=0;
}
previous_x=x;previous_y=y;pos=(pos+1U)%LAS_CAPACITY;
}
if(s->count>1) { indices[n]=previous_x;levels[n]=previous_y;moves[n++]=0; }
return n;
}
static size_t packet(uint32_t device,uint16_t body,const uint8_t *data,uint32_t uart,
uint32_t *raw,uint8_t *out,size_t cap) {
pcan_id_t id;pcan_frame_t frame;
if(device>15 || !raw || !out || cap<(uart?PCAN_FRAME_MAX:8U)) return 0;
memset(&id,0,sizeof(id));id.device_type=LAS_DEVICE_TYPE;id.device_id=(uint8_t)device;
id.priority=PCAN_PRIORITY_STANDARD;id.route=PCAN_ROUTE_FROM_DEVICE;
id.msg_type=PCAN_MSG_GAS;id.msg_body=body;
memset(&frame,0,sizeof(frame));frame.id=pcan_id_pack(&id);
frame.flags=PCAN_FLAG_IDE;frame.dlc=8;memcpy(frame.data,data,8);*raw=frame.id;
if(uart) return pcan_frame_encode(&frame,out,cap);
memcpy(out,data,8);return 8;
}
size_t las_metadata(uint32_t device,uint32_t session,uint32_t period,uint32_t uart,
uint32_t *id,uint8_t *out,size_t cap) {
uint8_t data[8];if(session>65535 || !period) return 0;
w16(data,LAS_TAG);w16(data+2,(uint16_t)session);w32(data+4,period);
return packet(device,LAS_GAS_META,data,uart,id,out,cap);
}
size_t las_data(uint32_t device,uint32_t session,uint32_t index,uint32_t sample,uint32_t uart,
uint32_t *id,uint8_t *out,size_t cap) {
uint8_t data[8];if(session>65535 || sample>65535) return 0;
w16(data,(uint16_t)session);w16(data+2,(uint16_t)sample);w32(data+4,index);
return packet(device,LAS_GAS_DATA,data,uart,id,out,cap);
}
void las_demo_step(void *ctx,uint32_t count) {
las_context *s=(las_context *)ctx;uint32_t i,bit,idx,raw;uint8_t packet_data[PCAN_FRAME_MAX];size_t n;
if(!s->metadata) {
n=las_metadata(s->device,1,1000000,1,&raw,packet_data,sizeof(packet_data));
las_uart(s,packet_data,n);
}
if(count>4096) count=4096;
for(i=0;i<count;++i) {
uint16_t a=0;idx=(uint32_t)s->next_index;
for(bit=0;bit<16;++bit) {
if((idx/(5U+bit*7U))&1U) a|=(uint16_t)(1U<<bit);
}
n=las_data(s->device,s->session,idx,a,1,&raw,packet_data,sizeof(packet_data));
las_uart(s,packet_data,n);
}
}