Добавить ядро цифрового Scope и адаптер декодеров DSView

This commit is contained in:
2026-11-09 02:03:13 +03:00
parent 6d59c3812c
commit f0edb70dbe
8 changed files with 474 additions and 0 deletions
+21
View File
@@ -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.
+60
View File
@@ -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
+75
View File
@@ -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
+44
View File
@@ -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.
+39
View File
@@ -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()
+7
View File
@@ -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" "$@"
+200
View File
@@ -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;
}
+28
View File
@@ -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')