Добавить ядро цифрового Scope и адаптер декодеров DSView
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
# Logic inputs in a native Scope UI
|
||||
|
||||
`include/logic_scope.h` is the portable, allocation-free C model for eight
|
||||
logic inputs and PWM duty reconstruction. The FPGA register mapping and
|
||||
framebuffer UI belong to the board application.
|
||||
|
||||
- `LogicScope`: bounded sample buffer, per-channel OFF/LOGIC/PWM mode,
|
||||
sample clock/divider, capture origin, and retained snapshot.
|
||||
- `logic_scope_plan`: chooses a count/divider for a requested duration;
|
||||
limits are 65,536 samples and divider 65,535. Longer requested intervals
|
||||
produce shorter captures; the UI must not stretch them to fill the view.
|
||||
- `logic_scope_pwm`: emits midpoint/value pairs for complete active-high
|
||||
PWM periods. Value is `2 * high_samples / period_samples - 1`. Initial
|
||||
and final incomplete periods are omitted; constant levels have no PWM
|
||||
period and emit no points. This is normalized modulation, not voltage.
|
||||
|
||||
The RK7020 consumer is `zynq_7020_OSC/demo/hdmi_usb_menu/`.
|
||||
Its `tests/test_logic_scope.c` checks the capture plan, phase-shifted PWM,
|
||||
constant inputs, menu selection, retained snapshots, and renderer bounds.
|
||||
|
||||
`include/scope_decoders.h` contains the portable DSView catalogue/configuration/annotation model; the isolated ARM adapter is in `tools/scope-decoders`. The framebuffer UI and UIO acquisition live in the Zynq board project.
|
||||
@@ -0,0 +1,60 @@
|
||||
#ifndef SET_LOGIC_SCOPE_H
|
||||
#define SET_LOGIC_SCOPE_H
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#define LOGIC_SCOPE_MAX 65536u
|
||||
typedef struct {
|
||||
uint8_t samples[LOGIC_SCOPE_MAX];
|
||||
unsigned mode[8]; /* 0 off, 1 logic, 2 PWM envelope */
|
||||
unsigned count, clock, divider, running, single, generation;
|
||||
double origin_s;
|
||||
char status[96];
|
||||
} LogicScope;
|
||||
|
||||
static inline unsigned logic_scope_channels(const LogicScope *s)
|
||||
{
|
||||
unsigned n=0;
|
||||
for (unsigned i=0;i<8;++i) n+=s->mode[i]!=0;
|
||||
return n;
|
||||
}
|
||||
|
||||
/* Capture the requested interval with the finest representable sample period. */
|
||||
static inline int logic_scope_plan(double span, unsigned clock, unsigned depth,
|
||||
unsigned *count, unsigned *divider)
|
||||
{
|
||||
if (!isfinite(span) || span<=0 || !clock || depth<2 || depth>LOGIC_SCOPE_MAX) return 0;
|
||||
double ticks=ceil(span*clock/depth);
|
||||
ticks=fmax(1,fmin(65536,ticks));
|
||||
*divider=(unsigned)ticks-1;
|
||||
*count=(unsigned)fmax(2,fmin(depth,ceil(span*clock/ticks)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
typedef void (*LogicScopePoint)(void *context, double time, double value);
|
||||
/* Ignore clipped first/last cycles. A constant level has no measurable period. */
|
||||
static inline unsigned logic_scope_pwm(const LogicScope *s, unsigned ch,
|
||||
LogicScopePoint point, void *context)
|
||||
{
|
||||
if (ch>=8 || !s->clock || s->count<2) return 0;
|
||||
unsigned rise=0,fall=0,have_rise=0,have_fall=0,n=0;
|
||||
unsigned previous=(s->samples[0]>>ch)&1u;
|
||||
double dt=(s->divider+1.0)/s->clock;
|
||||
for (unsigned i=1;i<s->count;++i) {
|
||||
unsigned level=(s->samples[i]>>ch)&1u;
|
||||
if (level!=previous) {
|
||||
if (level) {
|
||||
if (have_rise && have_fall) {
|
||||
point(context,s->origin_s+(rise+(i-rise)*0.5)*dt,
|
||||
2.0*(fall-rise)/(i-rise)-1.0);
|
||||
++n;
|
||||
}
|
||||
rise=i; have_rise=1; have_fall=0;
|
||||
} else if (have_rise) { fall=i; have_fall=1; }
|
||||
}
|
||||
previous=level;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,75 @@
|
||||
#ifndef SCOPE_DECODERS_H
|
||||
#define SCOPE_DECODERS_H
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#define SD_ITEMS 256
|
||||
#define SD_FIELDS 2048
|
||||
#define SD_RESULTS 4096
|
||||
typedef struct { char id[64],name[96],inputs[128],outputs[128]; int unavailable; } ScopeDecoder;
|
||||
typedef struct { int owner,channel,mandatory; char id[64],name[128],value[128],choices[512]; } ScopeDecoderField;
|
||||
typedef struct { uint64_t start,end; char decoder[64],row[96],text[256]; } ScopeAnnotation;
|
||||
typedef struct {
|
||||
ScopeDecoder items[SD_ITEMS]; ScopeDecoderField fields[SD_FIELDS]; ScopeAnnotation results[SD_RESULTS];
|
||||
unsigned order[SD_ITEMS],count,field_count,result_count; int open,view,page,selected,edit,request,busy;
|
||||
unsigned chain[8],chain_count,visible_fields[128],visible_count; double rate;
|
||||
char status[160],edit_text[128];
|
||||
} ScopeDecoders;
|
||||
static inline void sd_copy(char *to,size_t size,const char *from) { size_t n=strlen(from); if(n>=size)n=size-1; memcpy(to,from,n);to[n]=0; }
|
||||
static inline unsigned sd_split(char *line,char **out,unsigned cap) {
|
||||
unsigned n=0; char *p=line; while(n<cap) { out[n++]=p; char *end=strchr(p,'\t'); if(!end)break;*end=0;p=end+1; }
|
||||
if(n)out[n-1][strcspn(out[n-1],"\r\n")]=0; return n;
|
||||
}
|
||||
static inline int sd_find(const ScopeDecoders *s,const char *id) { for(unsigned i=0;i<s->count;++i)if(!strcmp(s->items[i].id,id))return (int)i;return -1; }
|
||||
static inline int sd_has(const char *list,const char *token) {
|
||||
size_t n=strlen(token);for(const char *p=list;*p;) {const char *e=strchr(p,'|');size_t k=e?(size_t)(e-p):strlen(p);if(k==n&&!strncmp(p,token,n))return 1;if(!e)break;p=e+1;}return 0;
|
||||
}
|
||||
static inline int sd_catalog(ScopeDecoders *s,const char *path) {
|
||||
FILE *f=fopen(path,"r");if(!f)return 0;s->count=s->field_count=0;char line[4096];
|
||||
while(fgets(line,sizeof(line),f)) {char *v[8];unsigned n=sd_split(line,v,8);
|
||||
if((!strcmp(v[0],"D")||!strcmp(v[0],"E"))&&n>=3&&s->count<SD_ITEMS) {
|
||||
ScopeDecoder *d=&s->items[s->count++];memset(d,0,sizeof(*d));sd_copy(d->id,sizeof(d->id),v[1]);sd_copy(d->name,sizeof(d->name),v[2]);d->unavailable=v[0][0]=='E';
|
||||
if(n>=5){sd_copy(d->inputs,sizeof(d->inputs),v[3]);sd_copy(d->outputs,sizeof(d->outputs),v[4]);}
|
||||
} else if((v[0][0]=='C'||v[0][0]=='O')&&n>=5&&s->field_count<SD_FIELDS) {
|
||||
int owner=sd_find(s,v[1]);if(owner<0)continue;ScopeDecoderField *d=&s->fields[s->field_count++];memset(d,0,sizeof(*d));d->owner=owner;d->channel=v[0][0]=='C';
|
||||
sd_copy(d->id,sizeof(d->id),v[2]);sd_copy(d->name,sizeof(d->name),v[3]);d->mandatory=d->channel&&atoi(v[4]);
|
||||
sd_copy(d->value,sizeof(d->value),d->channel?"-1":n>5?v[5]:"");if(n>6)sd_copy(d->choices,sizeof(d->choices),v[6]);
|
||||
}
|
||||
}fclose(f);for(unsigned i=0;i<s->count;++i){unsigned j=i;s->order[i]=i;while(j>0&&strcmp(s->items[s->order[j-1]].id,s->items[i].id)>0){s->order[j]=s->order[j-1];--j;}s->order[j]=i;}snprintf(s->status,sizeof(s->status),"%u DSView decoders / D0-D7 inputs",s->count);return s->count!=0;
|
||||
}
|
||||
static inline int sd_resolve(ScopeDecoders *s,unsigned id,unsigned depth) {
|
||||
if(depth>=8||id>=s->count||s->items[id].unavailable)return 0;
|
||||
ScopeDecoder *d=&s->items[id];if(sd_has(d->inputs,"logic")){s->chain[s->chain_count++]=id;return 1;}
|
||||
char inputs[128];sd_copy(inputs,sizeof(inputs),d->inputs);char *p=inputs;
|
||||
while(*p) {char *end=strchr(p,'|');if(end)*end=0;int base=sd_find(s,p);
|
||||
if(base>=0&&(unsigned)base!=id&&sd_resolve(s,(unsigned)base,depth+1)){s->chain[s->chain_count++]=id;return 1;}
|
||||
for(int direct=1;direct>=0;--direct)for(unsigned i=0;i<s->count;++i)if(i!=id&&sd_has(s->items[i].inputs,"logic")==direct&&sd_has(s->items[i].outputs,p)&&sd_resolve(s,i,depth+1)){s->chain[s->chain_count++]=id;return 1;}
|
||||
if(!end)break;p=end+1;
|
||||
}return 0;
|
||||
}
|
||||
static inline void sd_select(ScopeDecoders *s,unsigned id) {
|
||||
s->selected=(int)id;s->page=0;s->view=1;s->edit=-1;s->chain_count=s->visible_count=0;
|
||||
if(!sd_resolve(s,id,0)){sd_copy(s->status,sizeof(s->status),"No supported logic input stack / import failed");return;}
|
||||
unsigned next=0;
|
||||
for(unsigned c=0;c<s->chain_count;++c)for(unsigned f=0;f<s->field_count;++f)if(s->fields[f].owner==(int)s->chain[c]&&s->visible_count<128){
|
||||
s->visible_fields[s->visible_count++]=f;ScopeDecoderField *field=&s->fields[f];
|
||||
if(field->channel&&field->mandatory&&atoi(field->value)<0&&next<8)snprintf(field->value,sizeof(field->value),"%u",next++);
|
||||
}
|
||||
sd_copy(s->status,sizeof(s->status),"Tap a field to change. DECODE uses the last digital snapshot.");
|
||||
}
|
||||
static inline int sd_config(const ScopeDecoders *s,const char *path) {
|
||||
FILE *f=fopen(path,"w");if(!f)return 0;
|
||||
for(unsigned c=0;c<s->chain_count;++c){unsigned id=s->chain[c];fprintf(f,"[%s]\n",s->items[id].id);
|
||||
for(unsigned i=0;i<s->field_count;++i){const ScopeDecoderField *d=&s->fields[i];if(d->owner==(int)id)fprintf(f,"%s.%s=%s\n",d->channel?"ch":"opt",d->id,d->value);}}
|
||||
int error=ferror(f);return fclose(f)==0&&!error;
|
||||
}
|
||||
static inline int sd_results(ScopeDecoders *s,const char *path) {
|
||||
FILE *f=fopen(path,"r");if(!f)return 0;char line[2048];s->result_count=0;sd_copy(s->status,sizeof(s->status),"No annotations / check channel mapping and sample rate");
|
||||
while(fgets(line,sizeof(line),f)){char *v[7];unsigned n=sd_split(line,v,7);
|
||||
if(n>=6&&!strcmp(v[0],"A")&&s->result_count<SD_RESULTS){ScopeAnnotation *a=&s->results[s->result_count++];a->start=strtoull(v[1],NULL,10);a->end=strtoull(v[2],NULL,10);sd_copy(a->decoder,sizeof(a->decoder),v[3]);sd_copy(a->row,sizeof(a->row),v[4]);sd_copy(a->text,sizeof(a->text),v[5]);}
|
||||
else if(n>=2&&!strcmp(v[0],"ERROR"))sd_copy(s->status,sizeof(s->status),v[1]);
|
||||
else if(n>=3&&!strcmp(v[0],"DONE")&&s->result_count)snprintf(s->status,sizeof(s->status),"%u annotations / %s",s->result_count,v[2]);
|
||||
}fclose(f);s->view=2;s->page=0;return 1;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,44 @@
|
||||
# Scope DSView decoder worker
|
||||
|
||||
Headless adapter for the DSView `libsigrokdecode4DSL` fork. Runs as an isolated
|
||||
process using the existing ARM DSView Python 3.11/GLib runtime. It does not
|
||||
start Qt, own the FPGA or modify output pins.
|
||||
|
||||
`build_arm.py --help` lists toolchain/sysroot/source/output arguments. Prepare
|
||||
the patched DSView sources and runtime sysroot using the board project's DSView
|
||||
build first. The script compiles the decoder library from source, not cached
|
||||
DSView objects. Deploy `scope-decode-arm.elf` and `scope-decode` beside `dsview/`.
|
||||
The wrapper configures Python and the shared SETProtocol library.
|
||||
|
||||
```
|
||||
./scope-decode --catalog /path/to/decoders catalog.tsv
|
||||
./scope-decode --decode /path/to/decoders result.tsv 1:uart config.ini capture.bin
|
||||
python3 test_worker.py ./scope-decode /path/to/decoders
|
||||
```
|
||||
|
||||
Snapshot: `LDS1`, three little-endian uint32 values (clock Hz, divider, count),
|
||||
then count bytes, bits D0-D7. Sample rate = clock / (divider + 1).
|
||||
Maximum 65536 samples. Config is INI, one section per decoder ID, e.g.:
|
||||
|
||||
```ini
|
||||
[1:uart]
|
||||
ch.rxtx=0
|
||||
opt.baudrate=9600
|
||||
```
|
||||
|
||||
Missing optional channels are disconnected. Mandatory channels must be mapped
|
||||
to D0-D7. The worker resolves protocol stacks, prefers direct logic producers,
|
||||
and passes options to each layer. It emits tab-separated D/C/O catalogue records,
|
||||
E import errors, and A annotation records with start/end samples, decoder, row,
|
||||
text. Module names and decoder IDs are intentionally distinct (e.g. `0-uart`
|
||||
and `0:uart`). Numeric DSView annotations are exported as hexadecimal text.
|
||||
Results retain up to 4096 annotations and explicitly report truncation.
|
||||
|
||||
Catalogue and options come from the installed decoders, not a hand-written list.
|
||||
The board UI must enforce a process timeout; the included integration uses 30 s.
|
||||
`test_worker.py` checks both DSView UART variants with known bytes and timestamps.
|
||||
This does not imply every protocol or electrical interface has been tested.
|
||||
|
||||
The adapter is GPL-2.0-or-later; linking with DSView's GPL-3.0-or-later decoder
|
||||
library produces a GPL-3.0-or-later executable. Retain DSView/libsigrokdecode and
|
||||
Python/runtime notices when redistributing the runtime.
|
||||
@@ -0,0 +1,39 @@
|
||||
"""Build the headless DSView decoder worker against the prepared ARM runtime sysroot."""
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--cc', type=Path, required=True)
|
||||
parser.add_argument('--sysroot', type=Path, required=True)
|
||||
parser.add_argument('--dsview-source', type=Path, required=True)
|
||||
parser.add_argument('--fallback-include', type=Path)
|
||||
parser.add_argument('--output', type=Path, required=True)
|
||||
args = parser.parse_args()
|
||||
cc, sysroot, source, out = [p.resolve() for p in (args.cc, args.sysroot, args.dsview_source, args.output)]
|
||||
out.mkdir(parents=True, exist_ok=True)
|
||||
includes = [source/'common', source/'libsigrokdecode4DSL', sysroot/'usr/include/python3.11', sysroot/'usr/include/glib-2.0', sysroot/'usr/lib/arm-linux-gnueabihf/glib-2.0/include']
|
||||
flags = [str(cc), '--sysroot='+str(sysroot), '-mcpu=cortex-a9', '-O2', '-std=c11', '-D_DEFAULT_SOURCE', '-pthread', '-isystem', str(sysroot/'usr/include/arm-linux-gnueabihf')]
|
||||
if args.fallback_include:
|
||||
flags += ['-idirafter', str(args.fallback_include.resolve())]
|
||||
for path in includes:
|
||||
flags += ['-I', str(path)]
|
||||
objects = []
|
||||
files = sorted((source/'libsigrokdecode4DSL').glob('*.c')) + [source/'common/log/xlog.c', Path(__file__).with_name('scope_decode.c')]
|
||||
if len(files) < 10:
|
||||
raise RuntimeError('Prepare the DSView fork sources and ARM sysroot first')
|
||||
for path in files:
|
||||
target = out/('decoder-'+path.stem+'.o')
|
||||
subprocess.run(flags + ['-c', str(path), '-o', str(target)], check=True)
|
||||
objects.append(str(target))
|
||||
link = flags + objects
|
||||
for path in (sysroot/'usr/lib/arm-linux-gnueabihf', sysroot/'lib/arm-linux-gnueabihf', sysroot/'usr/lib/gcc/arm-linux-gnueabihf/12'):
|
||||
link += ['-B'+str(path)+'/', '-L'+str(path), '-Wl,-rpath-link,'+str(path)]
|
||||
subprocess.run(link + ['-lglib-2.0', '-lpython3.11', '-lm', '-lpthread', '-o', str(out/'scope-decode-arm.elf')], check=True)
|
||||
(out/'scope-decode').write_bytes(Path(__file__).with_name('scope-decode').read_bytes().replace(b'\r\n', b'\n'))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
here=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
||||
export PYTHONHOME="$here/dsview"
|
||||
export PYTHONDONTWRITEBYTECODE=1
|
||||
export SETPROTOCOL_LIBRARY="$here/dsview/lib/libsetprotocol.so"
|
||||
exec "$here/dsview/lib/ld-linux-armhf.so.3" --library-path "$here/dsview/lib" "$here/scope-decode-arm.elf" "$@"
|
||||
@@ -0,0 +1,200 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Headless adapter for the DSView libsigrokdecode fork. No Qt dependency.
|
||||
* Input snapshots use LDS1 + LE32 clock/divider/count + count byte samples.
|
||||
*/
|
||||
#include <Python.h>
|
||||
#include <libsigrokdecode.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
#include <dirent.h>
|
||||
|
||||
static FILE *output;
|
||||
static unsigned annotations;
|
||||
static void field(const char *text)
|
||||
{
|
||||
if (!text) return;
|
||||
for (;*text;++text) fputc(*text=='\t'||*text=='\n'||*text=='\r'?' ':*text,output);
|
||||
}
|
||||
static void variant(GVariant *v)
|
||||
{
|
||||
if (g_variant_is_of_type(v,G_VARIANT_TYPE_STRING)) field(g_variant_get_string(v,NULL));
|
||||
else { char *s=g_variant_print(v,FALSE); field(s); g_free(s); }
|
||||
}
|
||||
static void string_list(GSList *items)
|
||||
{
|
||||
for (GSList *p=items;p;p=p->next) { if (p!=items) fputc('|',output); field(p->data); }
|
||||
}
|
||||
static void catalog(const char *path)
|
||||
{
|
||||
PyGILState_STATE gil=PyGILState_Ensure();
|
||||
for (const GSList *p=srd_decoder_list();p;p=p->next) {
|
||||
struct srd_decoder *d=p->data;
|
||||
fputs("D\t",output); field(d->id); fputc('\t',output); field(d->name);
|
||||
fputc('\t',output); string_list(d->inputs); fputc('\t',output); string_list(d->outputs); fputc('\n',output);
|
||||
for (int optional=0;optional<2;++optional) for (GSList *c=optional?d->opt_channels:d->channels;c;c=c->next) {
|
||||
struct srd_channel *ch=c->data;
|
||||
fprintf(output,"C\t%s\t",d->id); field(ch->id); fputc('\t',output); field(ch->name);
|
||||
fprintf(output,"\t%d\n",!optional);
|
||||
}
|
||||
for (GSList *o=d->options;o;o=o->next) {
|
||||
struct srd_decoder_option *opt=o->data;
|
||||
fprintf(output,"O\t%s\t",d->id); field(opt->id); fputc('\t',output); field(opt->desc);
|
||||
fprintf(output,"\t%s\t",g_variant_get_type_string(opt->def)); variant(opt->def); fputc('\t',output);
|
||||
for (GSList *v=opt->values;v;v=v->next) { if (v!=opt->values) fputc('|',output); variant(v->data); }
|
||||
fputc('\n',output);
|
||||
}
|
||||
}
|
||||
DIR *dir=opendir(path); struct dirent *entry;
|
||||
if (dir) {
|
||||
while ((entry=readdir(dir))) {
|
||||
if (entry->d_name[0]=='.') continue;
|
||||
int loaded=0;
|
||||
for (const GSList *p=srd_decoder_list();p;p=p->next) { struct srd_decoder *d=p->data; const char *name=PyModule_GetName((PyObject *)d->py_mod); if(name && !strcmp(name,entry->d_name)) loaded=1; }
|
||||
if(loaded) continue;
|
||||
char *file=g_build_filename(path,entry->d_name,"pd.py",NULL);
|
||||
if (g_file_test(file,G_FILE_TEST_IS_REGULAR)) {
|
||||
PyObject *module=PyImport_ImportModule(entry->d_name); if(!module)PyErr_Print(); else Py_DECREF(module);
|
||||
fputs("E\t",output); field(entry->d_name); fputs("\tImport failed; see decoder log\n",output);
|
||||
}
|
||||
g_free(file);
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
PyGILState_Release(gil);
|
||||
}
|
||||
static struct srd_decoder *chain[8];
|
||||
static unsigned chain_count;
|
||||
static int resolve(struct srd_decoder *d,unsigned depth)
|
||||
{
|
||||
if (!d || depth>=8) return 0;
|
||||
for (unsigned i=0;i<chain_count;++i) if (chain[i]==d) return 0;
|
||||
for (GSList *i=d->inputs;i;i=i->next) if (!strcmp(i->data,"logic")) { chain[chain_count++]=d; return 1; }
|
||||
for (GSList *i=d->inputs;i;i=i->next) {
|
||||
/* Prefer the canonical decoder named after its output protocol. */
|
||||
struct srd_decoder *base=srd_decoder_get_by_id(i->data);
|
||||
if (base && base!=d && resolve(base,depth+1)) { chain[chain_count++]=d; return 1; }
|
||||
for (int direct=1;direct>=0;--direct) for (const GSList *p=srd_decoder_list();p;p=p->next) {
|
||||
base=p->data;
|
||||
int logic=0; for(GSList *q=base->inputs;q;q=q->next) if(!strcmp(q->data,"logic"))logic=1;
|
||||
if(logic!=direct)continue;
|
||||
if (base==d) continue;
|
||||
for (GSList *o=base->outputs;o;o=o->next) if (!strcmp(o->data,i->data) && resolve(base,depth+1)) {
|
||||
chain[chain_count++]=d; return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
static void annotation(struct srd_proto_data *data,void *context)
|
||||
{
|
||||
(void)context;
|
||||
struct srd_proto_data_annotation *a=data->data;
|
||||
if (++annotations>4096) return;
|
||||
const struct srd_decoder *d=data->pdo->di->decoder;
|
||||
const char *row="Data";
|
||||
for (GSList *r=d->annotation_rows;r;r=r->next) {
|
||||
struct srd_decoder_annotation_row *item=r->data;
|
||||
for (GSList *c=item->ann_classes;c;c=c->next)
|
||||
if (GPOINTER_TO_INT(c->data)==a->ann_class) row=item->desc;
|
||||
}
|
||||
fprintf(output,"A\t%llu\t%llu\t%s\t",(unsigned long long)data->start_sample,(unsigned long long)data->end_sample,d->id);
|
||||
field(row); fputc('\t',output);
|
||||
if (a->str_number_hex[0]) { fputs("0x",output); field(a->str_number_hex); }
|
||||
else if (a->ann_text && a->ann_text[0]) field(a->ann_text[0]);
|
||||
fputc('\n',output);
|
||||
}
|
||||
static GVariant *option_value(GVariant *original,const char *text)
|
||||
{
|
||||
if (g_variant_is_of_type(original,G_VARIANT_TYPE_STRING)) return g_variant_new_string(text);
|
||||
if (g_variant_is_of_type(original,G_VARIANT_TYPE_INT64)) {
|
||||
char *end; gint64 v=g_ascii_strtoll(text,&end,0); return *end?NULL:g_variant_new_int64(v);
|
||||
}
|
||||
if (g_variant_is_of_type(original,G_VARIANT_TYPE_DOUBLE)) {
|
||||
char *end; double v=g_ascii_strtod(text,&end); return *end||!isfinite(v)?NULL:g_variant_new_double(v);
|
||||
}
|
||||
if (g_variant_is_of_type(original,G_VARIANT_TYPE_BOOLEAN)) {
|
||||
if (!strcmp(text,"true") || !strcmp(text,"1")) return g_variant_new_boolean(TRUE);
|
||||
if (!strcmp(text,"false") || !strcmp(text,"0")) return g_variant_new_boolean(FALSE);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
static uint32_t le32(const unsigned char *p) { return (uint32_t)p[0]|(uint32_t)p[1]<<8|(uint32_t)p[2]<<16|(uint32_t)p[3]<<24; }
|
||||
static int decode(const char *id,const char *config,const char *snapshot)
|
||||
{
|
||||
if (!resolve(srd_decoder_get_by_id(id),0)) { fputs("ERROR\tNo logic input stack for this decoder\n",output); return 1; }
|
||||
GKeyFile *settings=g_key_file_new();
|
||||
if (!g_key_file_load_from_file(settings,config,G_KEY_FILE_NONE,NULL)) return 1;
|
||||
unsigned char header[16],samples[65536];
|
||||
FILE *input=fopen(snapshot,"rb"); if (!input) return 1;
|
||||
if (fread(header,1,16,input)!=16 || memcmp(header,"LDS1",4)) { fclose(input); return 1; }
|
||||
unsigned clock=le32(header+4),divider=le32(header+8),count=le32(header+12);
|
||||
if (!clock || divider>65535 || !count || count>65536 || fread(samples,1,count,input)!=count) { fclose(input); return 1; }
|
||||
fclose(input);
|
||||
struct srd_session *session=NULL;
|
||||
struct srd_decoder_inst *previous=NULL,*base=NULL;
|
||||
if (srd_session_new(&session)!=SRD_OK) return 1;
|
||||
int result=1;
|
||||
for (unsigned index=0;index<chain_count;++index) {
|
||||
struct srd_decoder *d=chain[index];
|
||||
GHashTable *options=g_hash_table_new_full(g_str_hash,g_str_equal,g_free,(GDestroyNotify)g_variant_unref);
|
||||
for (GSList *p=d->options;p;p=p->next) {
|
||||
struct srd_decoder_option *o=p->data;
|
||||
char *key=g_strconcat("opt.",o->id,NULL),*text=g_key_file_get_string(settings,d->id,key,NULL); g_free(key);
|
||||
if (text) {
|
||||
GVariant *value=option_value(o->def,text); g_free(text);
|
||||
if (!value) { fprintf(output,"ERROR\tInvalid option %s/%s\n",d->id,o->id); g_hash_table_destroy(options); goto done; }
|
||||
g_hash_table_insert(options,g_strdup(o->id),g_variant_ref_sink(value));
|
||||
}
|
||||
}
|
||||
struct srd_decoder_inst *di=srd_inst_new(session,d->id,options);
|
||||
g_hash_table_destroy(options); if (!di) goto done;
|
||||
if (!index) base=di;
|
||||
if (previous && srd_inst_stack(session,previous,di)!=SRD_OK) goto done;
|
||||
previous=di;
|
||||
if (!index) {
|
||||
GHashTable *channels=g_hash_table_new_full(g_str_hash,g_str_equal,g_free,(GDestroyNotify)g_variant_unref);
|
||||
for (int optional=0;optional<2;++optional) for (GSList *p=optional?d->opt_channels:d->channels;p;p=p->next) {
|
||||
struct srd_channel *ch=p->data; char *key=g_strconcat("ch.",ch->id,NULL);
|
||||
GError *error=NULL; int physical=g_key_file_get_integer(settings,d->id,key,&error); g_free(key);
|
||||
if (error) { g_error_free(error); physical=-1; }
|
||||
if (physical<0 && optional) continue;
|
||||
if (physical<0 || physical>7) { fprintf(output,"ERROR\tAssign %s/%s to D0-D7\n",d->id,ch->name); g_hash_table_destroy(channels); goto done; }
|
||||
g_hash_table_insert(channels,g_strdup(ch->id),g_variant_ref_sink(g_variant_new_int32(physical)));
|
||||
}
|
||||
int ret=srd_inst_channel_set_all(di,channels); g_hash_table_destroy(channels);
|
||||
if (ret!=SRD_OK) goto done;
|
||||
}
|
||||
}
|
||||
srd_session_metadata_set(session,SRD_CONF_SAMPLERATE,g_variant_new_uint64((uint64_t)llround(clock/(divider+1.0))));
|
||||
srd_pd_output_callback_add(session,SRD_OUTPUT_ANN,annotation,NULL);
|
||||
unsigned char planes[8][8192]; memset(planes,0,sizeof(planes));
|
||||
for (unsigned i=0;i<count;++i) for (unsigned ch=0;ch<8;++ch) if (samples[i]&(1u<<ch)) planes[ch][i/8]|=1u<<(i%8);
|
||||
const uint8_t **buffers=g_new0(const uint8_t *,base->dec_num_channels);
|
||||
uint8_t *constants=g_new0(uint8_t,base->dec_num_channels);
|
||||
for (int ch=0;ch<base->dec_num_channels;++ch) if (base->dec_channelmap[ch]>=0) buffers[ch]=planes[base->dec_channelmap[ch]];
|
||||
char *error=NULL;
|
||||
if (srd_session_start(session,&error)==SRD_OK && srd_session_send(session,0,count,buffers,constants,count,&error)==SRD_OK && srd_session_end(session,&error)==SRD_OK) result=0;
|
||||
if (error) { fputs("ERROR\t",output); field(error); fputc('\n',output); g_free(error); }
|
||||
/* Keep bitplanes alive until the decoder thread is joined. */
|
||||
srd_session_destroy(session); session=NULL; g_free(buffers); g_free(constants);
|
||||
fprintf(output,"DONE\t%u\t%s\n",annotations,annotations>4096?"truncated to 4096 annotations":"complete");
|
||||
done:
|
||||
if (session) srd_session_destroy(session);
|
||||
g_key_file_free(settings); return result;
|
||||
}
|
||||
int main(int argc,char **argv)
|
||||
{
|
||||
if (argc!=4 && argc!=7) return 2;
|
||||
output=fopen(argv[3],"w"); if (!output) return 2;
|
||||
srd_log_level(0);
|
||||
if (srd_init(argv[2])!=SRD_OK) { fclose(output); return 3; }
|
||||
srd_decoder_load_all();
|
||||
int result=0;
|
||||
if (argc==4 && !strcmp(argv[1],"--catalog")) catalog(argv[2]);
|
||||
else if (argc==7 && !strcmp(argv[1],"--decode")) result=decode(argv[4],argv[5],argv[6]);
|
||||
else result=2;
|
||||
fclose(output); srd_exit(); return result;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
"""Run a known UART vector through the real ARM DSView worker (on the board)."""
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import struct
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
worker = Path(sys.argv[1]).resolve()
|
||||
decoders = sys.argv[2]
|
||||
with tempfile.TemporaryDirectory(prefix='scope-worker-test-') as temp:
|
||||
root = Path(temp)
|
||||
samples = bytearray([1]*32)
|
||||
for value in (0x55, 0x41, 0xA3):
|
||||
for bit in [0]+[(value>>i)&1 for i in range(8)]+[1, 1]:
|
||||
samples.extend([bit]*16)
|
||||
samples.extend([1]*64)
|
||||
snapshot = root/'uart.bin'
|
||||
snapshot.write_bytes(b'LDS1'+struct.pack('<III', 153600, 0, len(samples))+samples)
|
||||
for decoder in ('0:uart', '1:uart'):
|
||||
config = root/'config.ini'
|
||||
config.write_text('['+decoder+']\nch.rxtx=0\nopt.baudrate=9600\n')
|
||||
output = root/'result.tsv'
|
||||
subprocess.run([str(worker), '--decode', decoders, str(output), decoder, str(config), str(snapshot)], check=True, timeout=30)
|
||||
rows = [line.split('\t') for line in output.read_text().splitlines()]
|
||||
data = [r for r in rows if len(r)>=6 and r[0]=='A' and r[4]=='RX/TX']
|
||||
assert [r[5] for r in data] == ['0x55', '0x41', '0xA3'], data
|
||||
assert [int(r[1]) for r in data] == [32, 208, 384], data
|
||||
print('PASS ARM worker: DSView 0:UART and 1:UART, 55 41 A3, exact sample timestamps')
|
||||
Reference in New Issue
Block a user