201 lines
8.3 KiB
C
201 lines
8.3 KiB
C
/*
|
|
* Эталонная композиция сервисов эмулятора: УМП, TMS, IGBT и генератор сигналов. Общая карта
|
|
* регистров маршрутизирует обращения; другой продукт может составить собственную карту через
|
|
* set_regmap.
|
|
*/
|
|
|
|
#include "set_emu_server.h"
|
|
#include "set_crc.h"
|
|
#include <string.h>
|
|
static const uint32_t baudrates[5]={9600,19200,38400,57600,115200};
|
|
uint32_t set_emu_server_baud(const set_emu_server *s) { return baudrates[s->config[2]]; }
|
|
static unsigned control_read(void *ctx,uint16_t off,uint16_t n,uint16_t *out)
|
|
{
|
|
set_emu_server *s=(set_emu_server *)ctx;uint16_t w[36]={0};unsigned i;
|
|
if(!((off==0&&n==4)||(off==16&&(n==8||n==10))||(off==32&&n==4)))return 2;
|
|
w[0]=0x454d;w[1]=1;w[2]=s->ump.enabled&1u;w[3]=s->ump.prev_command;
|
|
w[16]=0x5343;w[17]=3;
|
|
for(i=0;i<3;++i){w[18+i]=s->config[i];w[21+i]=s->staged[i];}
|
|
w[24]=s->config[3];w[25]=s->staged[3];
|
|
w[32]=0x4642;w[33]=1;w[34]=3;w[35]=SET_UMP_WORDS;
|
|
for(i=0;i<n;++i)out[i]=w[off+i];
|
|
return 0;
|
|
}
|
|
static unsigned control_write(void *ctx,uint16_t off,uint16_t v)
|
|
{
|
|
set_emu_server *s=(set_emu_server *)ctx;
|
|
if(off==2) {
|
|
if(v && s->config[3]!=0)return 6;
|
|
if(v>1)return 3;
|
|
s->ump.enabled=(uint8_t)((s->ump.enabled&~1u)|v);
|
|
if(!v)s->ump.scenario=0;
|
|
return 0;
|
|
}
|
|
if(off<21||off>25)return 2;
|
|
if(s->apply_delay)return 6;
|
|
if(off==24) {
|
|
if(v==0)memcpy(s->staged,s->config,sizeof(s->config));
|
|
else if(v==0xa55a) {
|
|
if(s->staged[0]==s->staged[1])return 3;
|
|
s->apply_delay=500;
|
|
} else return 3;
|
|
} else if(off==25) {
|
|
if(v>3 || (v==3&&!s->wave))return 3;
|
|
s->staged[3]=v;
|
|
} else {
|
|
if((off==21&&(!v||v>247))||(off==22&&(!v||v>255))||(off==23&&v>4))return 3;
|
|
s->staged[off-21]=v;
|
|
}
|
|
return 0;
|
|
}
|
|
static unsigned ump_read(void *ctx,uint16_t off,uint16_t n,uint16_t *out)
|
|
{
|
|
set_emu_server *s=(set_emu_server *)ctx;
|
|
return s->config[3]?6:set_ump_read(&s->ump,off,n,out);
|
|
}
|
|
static unsigned ump_write(void *ctx,uint16_t off,uint16_t v)
|
|
{
|
|
set_emu_server *s=(set_emu_server *)ctx;
|
|
return s->config[3]?6:set_ump_write(&s->ump,off,v);
|
|
}
|
|
static unsigned indexed_read(void *ctx,uint16_t off,uint16_t n,uint16_t *out)
|
|
{
|
|
set_emu_server *s=(set_emu_server *)ctx;
|
|
return s->config[3]?6:set_ump_indexed_read(&s->ump,off,n,out);
|
|
}
|
|
static unsigned tms_read(void *ctx,uint16_t off,uint16_t n,uint16_t *out)
|
|
{
|
|
set_emu_server *s=(set_emu_server *)ctx;
|
|
return set_tms_read(&s->tms,off,n,out);
|
|
}
|
|
static unsigned tms_write(void *ctx,uint16_t off,uint16_t v)
|
|
{
|
|
set_emu_server *s=(set_emu_server *)ctx;
|
|
return s->config[3]!=1?6:set_tms_write(&s->tms,off,v);
|
|
}
|
|
/* Status remains readable in every mode; only the selected service may write.
|
|
* Explicit stop commands remain available regardless of mode. */
|
|
static unsigned igbt_write(void *ctx,uint16_t off,uint16_t v)
|
|
{
|
|
set_emu_server *s=(set_emu_server *)ctx;
|
|
if(s->config[3]!=2 && !(off==2&&v==0))return 6;
|
|
return set_igbt_write(&s->igbt,off,v);
|
|
}
|
|
static unsigned igbt_read(void *ctx,uint16_t off,uint16_t n,uint16_t *out)
|
|
{
|
|
return set_igbt_read(&((set_emu_server *)ctx)->igbt,off,n,out);
|
|
}
|
|
static unsigned wave_write(void *ctx,uint16_t off,uint16_t v)
|
|
{
|
|
set_emu_server *s=(set_emu_server *)ctx;
|
|
if(s->config[3]!=3 && !(off==2&&v==0))return 6;
|
|
return set_wave_write(s->wave,off,v);
|
|
}
|
|
static unsigned wave_read(void *ctx,uint16_t off,uint16_t n,uint16_t *out)
|
|
{
|
|
return set_wave_read(((set_emu_server *)ctx)->wave,off,n,out);
|
|
}
|
|
static unsigned samples_write(void *ctx,uint16_t off,uint16_t v)
|
|
{
|
|
set_emu_server *s=(set_emu_server *)ctx;
|
|
return s->config[3]!=3?6:set_wave_samples_write(s->wave,off,v);
|
|
}
|
|
static unsigned samples_read(void *ctx,uint16_t off,uint16_t n,uint16_t *out)
|
|
{
|
|
return set_wave_samples_read(((set_emu_server *)ctx)->wave,off,n,out);
|
|
}
|
|
static void region(set_reg_region *r,uint16_t base,uint16_t words,uint16_t kind,
|
|
uint16_t version,void *ctx,set_reg_read_fn read,set_reg_write_fn write)
|
|
{
|
|
r->base=base;r->words=words;r->kind=kind;r->version=version;r->instance=0;
|
|
r->flags=(uint16_t)(SET_REG_READ|(write?SET_REG_WRITE:0));
|
|
r->context=ctx;r->read=read;r->write=write;
|
|
}
|
|
unsigned set_emu_server_init(set_emu_server *s,uint16_t *b0,uint16_t *b1,
|
|
uint16_t capacity,set_wave_state *wave,unsigned address,unsigned tms,
|
|
unsigned baud,unsigned can,unsigned enabled)
|
|
{
|
|
size_t n=0;
|
|
if(!s||!b0||!b1||b0==b1||capacity<2||capacity>819||
|
|
!address||address>247||!tms||tms>255||address==tms||baud>4||
|
|
!can||can>16||enabled>1)return 3;
|
|
if(wave&&(!wave->samples||wave->capacity<2||wave->capacity>SET_WAVE_MAX))return 3;
|
|
memset(s,0,sizeof(*s));s->wave=wave;
|
|
if(set_ump_init(&s->ump,b0,b1,capacity,(uint16_t)can,(uint8_t)enabled))return 3;
|
|
set_tms_init(&s->tms);set_igbt_init(&s->igbt);s->igbt.enabled=0;
|
|
s->config[0]=(uint16_t)address;s->config[1]=(uint16_t)tms;s->config[2]=(uint16_t)baud;
|
|
memcpy(s->staged,s->config,sizeof(s->config));
|
|
region(&s->regions[n++],0x1000,0x17c,SET_EMU_KIND_UMP,2,s,ump_read,ump_write);
|
|
region(&s->regions[n++],SET_EMU_CONTROL_BASE,36,SET_EMU_KIND_CONTROL,3,s,control_read,control_write);
|
|
region(&s->regions[n++],SET_EMU_IGBT_BASE,SET_IGBT_WORDS,SET_EMU_KIND_IGBT,3,s,igbt_read,igbt_write);
|
|
region(&s->regions[n++],SET_EMU_TMS_BASE,SET_TMS_WORDS,SET_EMU_KIND_TMS,1,s,tms_read,tms_write);
|
|
region(&s->regions[n++],SET_EMU_INDEXED_BASE,(uint16_t)(capacity*SET_UMP_WORDS),
|
|
SET_EMU_KIND_UMP_DATA,1,s,indexed_read,0);
|
|
if(wave) {
|
|
region(&s->regions[n++],SET_WAVE_BASE,9,SET_EMU_KIND_WAVE,3,s,wave_read,wave_write);
|
|
region(&s->regions[n++],SET_WAVE_DATA,(uint16_t)wave->capacity,
|
|
SET_EMU_KIND_WAVE_DATA,1,s,samples_read,samples_write);
|
|
}
|
|
return set_regmap_init(&s->map,s->regions,n);
|
|
}
|
|
void set_emu_server_tick(set_emu_server *s,uint8_t pins)
|
|
{
|
|
if(s->apply_delay&&--s->apply_delay==0) {
|
|
if(s->config[3]!=s->staged[3]) {
|
|
/* Stop every executor before selecting the new one. No implicit start. */
|
|
if(s->wave) {
|
|
if(s->wave->port.stop)s->wave->port.stop(s->wave->port.context);
|
|
s->wave->running=0;s->wave->ready=0;
|
|
}
|
|
set_igbt_init(&s->igbt);s->igbt.enabled=0;
|
|
set_tms_init(&s->tms);s->ump.state=3;s->ump.post=0;s->ump.scenario=0;
|
|
s->ump.prev_command=0;s->ump.prev_output=0;
|
|
}
|
|
memcpy(s->config,s->staged,sizeof(s->config));
|
|
}
|
|
if(s->config[3]==1)set_tms_tick(&s->tms);
|
|
else if(s->config[3]==0)set_ump_tick(&s->ump);
|
|
else if(s->config[3]==2)set_igbt_tick(&s->igbt,pins);
|
|
}
|
|
static int inactive_ump(const set_emu_server *s,uint16_t a)
|
|
{
|
|
return s->config[3]&&((a>=0x1000&&a<0x1200)||
|
|
(a>=SET_EMU_INDEXED_BASE&&a<(uint32_t)SET_EMU_INDEXED_BASE+s->ump.capacity*SET_UMP_WORDS));
|
|
}
|
|
size_t set_emu_server_rtu(set_emu_server *s,const uint8_t *r,size_t n,uint8_t *out,size_t cap)
|
|
{
|
|
if(!s||!r||!n)return 0;
|
|
if(r[0]==s->config[1])
|
|
return s->config[3]==1?set_tms_rtu(&s->tms,s->config[1],r,n,out,cap):0;
|
|
if(n==8&&inactive_ump(s,(uint16_t)(((unsigned)r[2]<<8)|r[3])))return 0;
|
|
return set_regmap_rtu(&s->map,s->config[0],r,n,out,cap);
|
|
}
|
|
size_t set_emu_server_can(set_emu_server *s,const uint8_t *r,size_t n,
|
|
uint16_t *out,size_t cap,unsigned *token,unsigned *error)
|
|
{
|
|
if(token)*token=0;
|
|
if(error)*error=0;
|
|
if(!s||!r||n!=8||inactive_ump(s,(uint16_t)(((unsigned)r[0]<<8)|r[1])))return 0;
|
|
return set_regmap_can(&s->map,r,n,out,cap,token,error);
|
|
}
|
|
size_t set_emu_stream_feed(set_emu_stream *st,set_emu_server *s,uint8_t byte,
|
|
uint32_t now,uint8_t *out,size_t cap)
|
|
{
|
|
size_t length,n;
|
|
if(!st||!s||!out||cap<253)return 0;
|
|
if((uint32_t)(now-st->last_byte)>1000u||st->used>=sizeof(st->data))st->used=0;
|
|
st->last_byte=now;st->data[st->used++]=byte;
|
|
while(st->used>=8) {
|
|
length=st->data[0]==s->config[1]&&st->data[1]==65?18u:8u;
|
|
if(st->used<length)return 0;
|
|
if(set_crc16_modbus(st->data,length)) {
|
|
--st->used;memmove(st->data,st->data+1,st->used);continue;
|
|
}
|
|
n=set_emu_server_rtu(s,st->data,length,out,cap);
|
|
st->used=(uint8_t)(st->used-length);
|
|
memmove(st->data,st->data+length,st->used);
|
|
if(n)return n;
|
|
}
|
|
return 0;
|
|
}
|