Merge branch 'main' of github.com:rdolbeau/VintageBusFPGA_Common into main

This commit is contained in:
Romain Dolbeau
2023-09-16 15:11:05 +02:00
16 changed files with 943 additions and 0 deletions

View File

@@ -0,0 +1,41 @@
RETRO68=/home/dolbeau/Retro68/build/toolchain
AS=${RETRO68}/bin/m68k-apple-macos-as
CC=${RETRO68}/bin/m68k-apple-macos-gcc
LD=${RETRO68}/bin/m68k-apple-macos-ld
STRIP=${RETRO68}/bin/m68k-apple-macos-strip
OBJCOPY=${RETRO68}/bin/m68k-apple-macos-objcopy
NM=${RETRO68}/bin/m68k-apple-macos-nm
HOSTCC=gcc
HOSTCFLAGS=-O2
ARCHFLAGS=-march=68020 -mcpu=68020
CFLAGS=-O2 -mpcrel
GENLINK=../patcher/genlink
PATCHER=../patcher/patcher
all: IIsi.ROM
show: rompatch.elf
$(NM) $< | sort
IIsi.ROM: ../IIsiRemoveChecksumCheck/IIsi.ROM rompatch.raw input.txt
/bin/cp ../IIsiRemoveChecksumCheck/IIsi.ROM IIsi.ROM
${PATCHER} -i rompatch.raw -p IIsi.ROM -d input.txt
linker.ld: input.txt rompatch.s
${GENLINK} -d $< >| $@
echo $(shell for X in `grep .section rompatch.s | awk '{ print $$2 }' `; do grep -q $$X linker.ld || echo " $$X has no entry in linker.ld" && /bin/false; done)
rompatch.o: rompatch.s
${AS} ${ARCHFLAGS} $< -o $@ -a > rompatch.l
rompatch.elf: linker.ld rompatch.o ${CSRC:.c=.o} # linker script must be first
${LD} -o $@ -T $^
rompatch.raw: rompatch.elf
${OBJCOPY} $^ $@ --input-target=elf32-m68k --output-target=binary
clean:
rm -f res.inc ${CSRC_ASM} *.o rompatch.srec rompatch.raw rompatch.dir rompatch.l linker.ld rompatch.elf

View File

@@ -0,0 +1,3 @@
0x00396e, 32, raminfo
0x003cc4, 316, gary
0x0419e8, 20, findinfopatch

View File

@@ -0,0 +1,51 @@
MAPBASE=0x20000000
MAPSIZE=0x0f000000
MAPEND=MAPBASE+MAPSIZE
/* ************************************************************************ */
/* updated table */
.section .text.raminfo
.long MAPBASE
.long MAPEND
.long 0x04000000
.long 0x08000000
.long 0x00000000
.long 0x04000000
.long 0xFFFFFFFF
.long 0xFFFFFFFF
/* ************************************************************************ */
.section .text.gary
fixchunk: /* 316 bytes available */
/* recreate the table but with one more chunk, as the original code assumes two chunks and turns them into three */
move.l %D4,(%A1)+
move.l %D5,(%A1)+
move.l %D2,(%A1)+
move.l %D3,(%A1)+
add.l %D5,%D4
move.l %D4,(%A1)+
move.l %D1,(%A1)+
/* here comes the bonus */
move.l #MAPBASE,(%A1)+
move.l #MAPSIZE,(%A1)+
/* here ends the bonus */
moveq #-1,%D4
move.l %D4,(%A1)+
move.l %D4,(%A1)+
jmp (%pc,returnfindinfopatch)
/* ************************************************************************ */
.section .text.findinfopatch
findinfopatch: /* 20 bytes available */
jmp (%pc,fixchunk)
.section .text.returnfindinfopatch
returnfindinfopatch:
/* */
.end

View File

@@ -0,0 +1,41 @@
RETRO68=/home/dolbeau/Retro68/build/toolchain
AS=${RETRO68}/bin/m68k-apple-macos-as
CC=${RETRO68}/bin/m68k-apple-macos-gcc
LD=${RETRO68}/bin/m68k-apple-macos-ld
STRIP=${RETRO68}/bin/m68k-apple-macos-strip
OBJCOPY=${RETRO68}/bin/m68k-apple-macos-objcopy
NM=${RETRO68}/bin/m68k-apple-macos-nm
HOSTCC=gcc
HOSTCFLAGS=-O2
ARCHFLAGS=-march=68020 -mcpu=68020
CFLAGS=-O2 -mpcrel
GENLINK=../patcher/genlink
PATCHER=../patcher/patcher
all: IIsi.ROM
show: rompatch.elf
$(NM) $< | sort
IIsi.ROM: ../IIsi.ROM rompatch.raw input.txt
/bin/cp ../IIsi.ROM IIsi.ROM
${PATCHER} -i rompatch.raw -p IIsi.ROM -d input.txt
linker.ld: input.txt rompatch.s
${GENLINK} -d $< >| $@
echo $(shell for X in `grep .section rompatch.s | awk '{ print $$2 }' `; do grep -q $$X linker.ld || echo " $$X has no entry in linker.ld" && /bin/false; done)
rompatch.o: rompatch.s
${AS} ${ARCHFLAGS} $< -o $@ -a > rompatch.l
rompatch.elf: linker.ld rompatch.o ${CSRC:.c=.o} # linker script must be first
${LD} -o $@ -T $^
rompatch.raw: rompatch.elf
${OBJCOPY} $^ $@ --input-target=elf32-m68k --output-target=binary
clean:
rm -f res.inc ${CSRC_ASM} *.o rompatch.srec rompatch.raw rompatch.dir rompatch.l linker.ld rompatch.elf

View File

@@ -0,0 +1 @@
0x0464e2, 10, removecheksumcheck

View File

@@ -0,0 +1,12 @@
.section .text.removecheksumcheck
nops:
nop
nop
nop
nop
nop
.section .text.returnremovecheksumcheck
.end

12
RomPatcher/README.md Normal file
View File

@@ -0,0 +1,12 @@
# RomPatcher
This is a small set of tools to help with ROM patching. A single input file made up of lines \<address\>,\<size\>,\<name\> describes which area need patching. <size> can be 0 for e.g. calls to exsiting functions.
One tool generates linked file to place things where they need to be in the compiled binary.
One tool copies the relevant area from the generated binary to the file that needs to be patched.
An assembly source file can then be used to write the patch, placing code in the appropriate sections as per the linker file. They are then patched into the final file.
Example:
* IIsiRemoveChecksumCheck: replace a few instructions in a Macintosh IIsi ROM file to disable the checksum test (thus allowing further patching)
* IIsiExtraMemoryi: patch the memory chunk table and some code to enable an extra area of memory in a IIsi ROM, usable with e.g. the IIsiFPGA

View File

@@ -0,0 +1,50 @@
PATCHERSRC=patcher.c
PATCHEROBJ=$(PATCHERSRC:.cpp=.o)
PATCHERDEP=$(PATCHERSRC:.cpp=.d)
GENLINKSRC=genlink.c
GENLINKOBJ=$(GENLINKSRC:.cpp=.o)
GENLINKDEP=$(GENLINKSRC:.cpp=.d)
OOBJ=parser_par.o parser_lex.o
LEX=flex
YACC=bison -d #--report-file=bison.log --report=all
CC=gcc
CFLAGS=-O2
all: patcher genlink
%.o: %.c
$(CC) $(CFLAGS) $< -c -o $@
patcher: $(PATCHEROBJ) $(OOBJ)
$(CC) $(CFLAGS) $^ -o $@
genlink: $(GENLINKOBJ) $(OOBJ)
$(CC) $(CFLAGS) $^ -o $@
parser_par.h: parser_par.o
parser_par.o: parser_par.y
$(YACC) -o $(<:%.y=%.c) $<
$(CC) $(CFLAGS) $(<:%.y=%.c) -c -o $@
parser_lex.o: parser_lex.l parser_par.h
$(LEX) -o $(<:%.l=%.c) $<
$(CC) $(CFLAGS) $(<:%.l=%.c) -c -o $@
%.d: %.cpp
$(CXX) -MM $< -o $@
clean:
rm -f $(PATCHEROBJ) patcher
veryclean:
rm -f $(PATCHEROBJ) patcher *~ parser_lex.c parser_par.c *.d
ultraclean:
rm -f $(PATCHEROBJ) patcher *~ parser_lex.c parser_par.c *.d *.scala
## avoid builtin rule for .o
.SUFFIXES:
SUFFIXES :=
%.o:

View File

@@ -0,0 +1,76 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include "parser.h"
#include "parser_par.h"
extern FILE *yyin, *yyout;
extern int yydebug;
int main(int argc, char **argv) {
char* descfile = NULL;
FILE *myfile;
int c;
azone zones[32];
int nz = 0, i;
while ((c = getopt(argc, argv, "d:")) != -1) {
switch(c) {
default:
fprintf(stderr, "oups : %c\n", c);
exit(-1);
break;
case 'd':
descfile = strndup(optarg, 512);
break;
}
}
if (descfile == NULL) {
fprintf(stderr, "no desc\n");
exit(-4);
}
{
myfile = fopen(descfile, "r");
if (!myfile) {
fprintf(stderr, "no desc file\n");
exit(-5);
}
yyin = myfile;
yyparse(zones, &nz);
fclose(myfile);
fprintf(stderr, "Found %d entries\n", nz);
}
fprintf(stdout,
"OUTPUT_FORMAT(\"elf32-m68k\");\n"
"SECTIONS {\n"
" .text : {\n");
for (i = 0 ; i < nz; i++) {
fprintf(stdout, " . = 0x%06x;\n", zones[i].address);
fprintf(stdout, " *(.text.%s)\n", zones[i].name);
if (zones[i].size > 0) {
fprintf(stdout, " . = 0x%06x;\n", zones[i].address + zones[i].size);
fprintf(stdout, " *(.text.return%s)\n", zones[i].name);
}
fprintf(stdout, "\n");
}
fprintf(stdout, " }\n}\n");
return 0;
}

View File

@@ -0,0 +1,10 @@
#ifndef __PATCHER_H__
#define __PATCHER_H__
typedef struct {
unsigned long address;
unsigned long size;
char* name;
} azone;
#endif // __PATCHER_H__

View File

@@ -0,0 +1,34 @@
%{
/*
* Copyright (c) 2023 Romain Dolbeau <romain@dolbeau.org>
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "parser.h"
#include "parser_par.h"
%}
DIGIT [0-9]
HEXPREFIX "0x"
HEXDIGIT [0-9a-fA-F]
SPACE [ \t]
FCHARNAME [[:alpha:]]
CHARNAME [[:alnum:]_]
%%
{HEXPREFIX}{HEXDIGIT}{HEXDIGIT}* { yylval.num = strtol(yytext, NULL, 16); return NUM; }
{DIGIT}{DIGIT}* { yylval.num = strtol(yytext, NULL, 10); return NUM; }
{FCHARNAME}{CHARNAME}* { yylval.string = strdup(yytext); return NAME; }
, { return yytext[0]; }
\n { return yytext[0]; }
{SPACE}+ { }
%%

View File

@@ -0,0 +1,46 @@
%{
/*
* Copyright (c) 2023 Romain Dolbeau <romain@dolbeau.org>
* MIT License
* See the LICENSE file at the top level of this software distribution for details.
*/
#include <stdio.h>
#include <stdlib.h>
#define YYDEBUG 1
#include "parser.h"
%}
%parse-param {azone *zones} {int *nz}
%union
{
unsigned long num;
char* string;
}
%token <num> NUM
%token <num> NAME
%%
input: /* empty */ { }
| zone input { }
;
zone:
NUM ',' NUM ',' NAME { /* printf("0x%08lx %ld\n", $1, $3); */ zones[*nz].address = $1; zones[*nz].size = $3; zones[*nz].name = $5; (*nz)++; }
| '\n'
;
%%
int
yyerror(char *s)
{
fprintf(stderr, "error: %s\n", s);
return(0);
}
int
yywrap(void)
{
return(-1);
}

View File

@@ -0,0 +1,146 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include "parser.h"
#include "parser_par.h"
extern FILE *yyin, *yyout;
extern int yydebug;
int main(int argc, char **argv) {
char* descfile = NULL;
char* patchedfile = NULL;
char* inputfile = NULL;
FILE *myfile;
int c;
azone zones[32];
int nz = 0, i;
unsigned long j;
char *input;
size_t input_size;
char *patched;
size_t patched_size;
while ((c = getopt(argc, argv, "d:i:p:")) != -1) {
switch(c) {
default:
fprintf(stderr, "oups : %c\n", c);
exit(-1);
break;
case 'd':
descfile = strndup(optarg, 512);
break;
case 'i':
inputfile = strndup(optarg, 512);
break;
case 'p':
patchedfile = strndup(optarg, 512);
break;
}
}
if (descfile == NULL) {
fprintf(stderr, "no desc\n");
exit(-4);
}
if (inputfile == NULL) {
fprintf(stderr, "no input\n");
exit(-2);
}
if (patchedfile == NULL) {
fprintf(stderr, "nothing to patch\n");
exit(-3);
}
{
myfile = fopen(descfile, "r");
if (!myfile) {
fprintf(stderr, "no desc file\n");
exit(-5);
}
yyin = myfile;
yyparse(zones, &nz);
fclose(myfile);
fprintf(stdout, "Found %d entries\n", nz);
}
{
int fd;
fd = open(inputfile, O_RDONLY | O_EXCL);
if (fd == -1) {
fprintf(stderr, "no input file\n");
exit(-6);
}
struct stat statbuf;
int err = fstat(fd, &statbuf);
if (err < 0){
fprintf(stderr, "no input file stat\n");
close(fd);
exit(-7);
}
input_size = statbuf.st_size;
input = mmap(NULL, input_size, PROT_READ, MAP_SHARED, fd, 0);
if (input == MAP_FAILED){
fprintf(stderr, "no input file map (%p for %s %zd: %d / %s)\n", input, inputfile, input_size, errno, strerror(errno));
close(fd);
exit(-8);
}
close(fd);
}
{
int fd;
fd = open(patchedfile, O_RDWR | O_EXCL);
if (fd == -1) {
fprintf(stderr, "no patched file\n");
exit(-6);
}
struct stat statbuf;
int err = fstat(fd, &statbuf);
if (err < 0){
fprintf(stderr, "no patched file stat\n");
close(fd);
exit(-7);
}
patched_size = statbuf.st_size;
patched = mmap(NULL, patched_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (patched == MAP_FAILED){
fprintf(stderr, "no patched file map\n");
close(fd);
exit(-8);
}
close(fd);
}
for (i = 0 ; i < nz; i++) {
if (zones[i].size > 0) {
fprintf(stdout, "Patching [%08p:%08p[ (%s)\n", (void*)zones[i].address, (void*)(zones[i].address + zones[i].size), zones[i].name);
for (j = zones[i].address; j < zones[i].address + zones[i].size; j++) {
patched[j] = input[j];
}
} else {
fprintf(stdout, "Not patching [%08p[ (%s)\n", (void*)zones[i].address, zones[i].name);
}
}
munmap(input, input_size);
munmap(patched, patched_size);
return 0;
}

152
Ziscreen.py Normal file
View File

@@ -0,0 +1,152 @@
from migen import *
from migen.genlib.fifo import *
from litex.soc.interconnect.csr import *
class Ziscreen(Module, AutoCSR):
def __init__(self, platform, wb, width=1920, height=1080, depth=8, fifo=None):
default_address = (0x8f800000) // 4
last_address = (0x8f800000 + (width-8) + width*1080) // 4
address_increment = (width) // 4
base_address = Signal(30, reset = default_address)
cur_address = Signal(30)
rowcnt = Signal(5)
print(f"TRACE: displaying @ 0x{default_address*4:08x}")
cur_nib_idx = Signal(3)
cur_line_idx = Signal(3)
wrapped = Signal()
val = Signal(32)
cur_nib = Signal(4)
self.comb += [
Case((cur_nib_idx ^ 1), { ## nibble-swap for proper order
x: cur_nib.eq(val[(4*x):(4*x+4)]) for x in range(0, 8)
}),
]
cur_nib_decoded = Signal(8)
self.submodules.fsm = fsm = FSM(reset_state="Reset")
if (True):
#saw_ongoing = Signal()
#saw_readable= Signal()
#self.sync += [
# If(~fsm.ongoing("Idle"),
# saw_ongoing.eq(1),
# ),
# If(fifo.readable,
# saw_readable.eq(1),
# ),
#]
led0 = platform.request("user_led", 0)
led1 = platform.request("user_led", 1)
led2 = platform.request("user_led", 2)
led3 = platform.request("user_led", 3)
led4 = platform.request("user_led", 4)
led5 = platform.request("user_led", 5)
led6 = platform.request("user_led", 6)
led7 = platform.request("user_led", 7)
self.comb += [
led0.eq(~fsm.ongoing("Idle")),
led1.eq(fifo.readable),
led2.eq(0),
led3.eq(0),
#led4.eq(saw_ongoing),
#led5.eq(saw_readable),
led4.eq(0),
led5.eq(wb.ack),
led6.eq(wb.stb),
led7.eq(wb.cyc),
]
self.comb += [
wb.we.eq(1),
wb.sel.eq(0xF),
wb.adr.eq(cur_address),
]
fsm.act("Reset",
NextState("Idle"),
)
fsm.act("Idle",
If(base_address >= last_address,
NextValue(base_address, default_address),
NextValue(wrapped, 1 ^ wrapped),
NextValue(rowcnt, 0),
).Elif(rowcnt == 30,
NextValue(rowcnt, 0),
NextValue(base_address, base_address + 7*address_increment),
).Elif(fifo.readable,
NextValue(val, fifo.dout),
NextValue(cur_line_idx, 0),
NextValue(cur_nib_idx, 0),
NextValue(cur_address, base_address),
fifo.re.eq(1),
NextState("StartWriteLow"),
),
)
fsm.act("StartWriteLow",
wb.cyc.eq(1),
wb.stb.eq(1),
wb.dat_w.eq(Cat(Replicate(wrapped ^ cur_nib_decoded[7], 8), Replicate(wrapped ^ cur_nib_decoded[6], 8), Replicate(wrapped ^ cur_nib_decoded[5], 8), Replicate(wrapped ^ cur_nib_decoded[4], 8))),
If(wb.ack,
NextValue(cur_address, cur_address + 1),
NextState("StartWriteHigh"),
),
)
fsm.act("StartWriteHigh",
wb.cyc.eq(1),
wb.stb.eq(1),
wb.dat_w.eq(Cat(Replicate(wrapped ^ cur_nib_decoded[3], 8), Replicate(wrapped ^ cur_nib_decoded[2], 8), Replicate(wrapped ^ cur_nib_decoded[1], 8), Replicate(wrapped ^ cur_nib_decoded[0], 8))),
If(wb.ack,
If((cur_line_idx == 7) & (cur_nib_idx == 7),
NextValue(base_address, base_address + 16 - (address_increment*7) ),
NextValue(rowcnt, rowcnt + 1),
NextState("Idle"),
).Elif(cur_nib_idx == 7,
NextValue(base_address, base_address + address_increment),
NextValue(cur_address, base_address + address_increment),
NextValue(cur_nib_idx, 0),
NextValue(cur_line_idx, cur_line_idx + 1),
NextState("StartWriteLow"),
).Else(
NextValue(cur_address, cur_address + 1),
NextValue(cur_nib_idx, cur_nib_idx + 1),
NextState("StartWriteLow"),
)
),
)
self.comb += [
Case((cur_line_idx), {
0: cur_nib_decoded.eq(0),
7: cur_nib_decoded.eq(0),
1: Case(cur_nib, {
0:cur_nib_decoded.eq(0x18), 1:cur_nib_decoded.eq(0x10), 2:cur_nib_decoded.eq(0x18), 3:cur_nib_decoded.eq(0x38), 4:cur_nib_decoded.eq(0x20), 5:cur_nib_decoded.eq(0x3c), 6:cur_nib_decoded.eq(0x1c), 7:cur_nib_decoded.eq(0x3c), 8:cur_nib_decoded.eq(0x18), 9:cur_nib_decoded.eq(0x18), 10:cur_nib_decoded.eq(0x18), 11:cur_nib_decoded.eq(0x38), 12:cur_nib_decoded.eq(0x18), 13:cur_nib_decoded.eq(0x38), 14:cur_nib_decoded.eq(0x3c), 15:cur_nib_decoded.eq(0x3c),
}),
2: Case(cur_nib, {
0:cur_nib_decoded.eq(0x24), 1:cur_nib_decoded.eq(0x30), 2:cur_nib_decoded.eq(0x24), 3:cur_nib_decoded.eq(0x04), 4:cur_nib_decoded.eq(0x28), 5:cur_nib_decoded.eq(0x20), 6:cur_nib_decoded.eq(0x20), 7:cur_nib_decoded.eq(0x04), 8:cur_nib_decoded.eq(0x24), 9:cur_nib_decoded.eq(0x24), 10:cur_nib_decoded.eq(0x24), 11:cur_nib_decoded.eq(0x24), 12:cur_nib_decoded.eq(0x24), 13:cur_nib_decoded.eq(0x24), 14:cur_nib_decoded.eq(0x20), 15:cur_nib_decoded.eq(0x20),
}),
3: Case(cur_nib, {
0:cur_nib_decoded.eq(0x2c), 1:cur_nib_decoded.eq(0x10), 2:cur_nib_decoded.eq(0x04), 3:cur_nib_decoded.eq(0x18), 4:cur_nib_decoded.eq(0x28), 5:cur_nib_decoded.eq(0x38), 6:cur_nib_decoded.eq(0x38), 7:cur_nib_decoded.eq(0x08), 8:cur_nib_decoded.eq(0x18), 9:cur_nib_decoded.eq(0x24), 10:cur_nib_decoded.eq(0x24), 11:cur_nib_decoded.eq(0x38), 12:cur_nib_decoded.eq(0x20), 13:cur_nib_decoded.eq(0x24), 14:cur_nib_decoded.eq(0x38), 15:cur_nib_decoded.eq(0x38),
}),
4: Case(cur_nib, {
0:cur_nib_decoded.eq(0x34), 1:cur_nib_decoded.eq(0x10), 2:cur_nib_decoded.eq(0x18), 3:cur_nib_decoded.eq(0x04), 4:cur_nib_decoded.eq(0x3c), 5:cur_nib_decoded.eq(0x04), 6:cur_nib_decoded.eq(0x24), 7:cur_nib_decoded.eq(0x10), 8:cur_nib_decoded.eq(0x24), 9:cur_nib_decoded.eq(0x1c), 10:cur_nib_decoded.eq(0x3c), 11:cur_nib_decoded.eq(0x24), 12:cur_nib_decoded.eq(0x20), 13:cur_nib_decoded.eq(0x24), 14:cur_nib_decoded.eq(0x20), 15:cur_nib_decoded.eq(0x20),
}),
5: Case(cur_nib, {
0:cur_nib_decoded.eq(0x24), 1:cur_nib_decoded.eq(0x10), 2:cur_nib_decoded.eq(0x20), 3:cur_nib_decoded.eq(0x04), 4:cur_nib_decoded.eq(0x08), 5:cur_nib_decoded.eq(0x04), 6:cur_nib_decoded.eq(0x24), 7:cur_nib_decoded.eq(0x10), 8:cur_nib_decoded.eq(0x24), 9:cur_nib_decoded.eq(0x04), 10:cur_nib_decoded.eq(0x24), 11:cur_nib_decoded.eq(0x24), 12:cur_nib_decoded.eq(0x24), 13:cur_nib_decoded.eq(0x24), 14:cur_nib_decoded.eq(0x20), 15:cur_nib_decoded.eq(0x20),
}),
6: Case(cur_nib, {
0:cur_nib_decoded.eq(0x18), 1:cur_nib_decoded.eq(0x38), 2:cur_nib_decoded.eq(0x3c), 3:cur_nib_decoded.eq(0x38), 4:cur_nib_decoded.eq(0x08), 5:cur_nib_decoded.eq(0x38), 6:cur_nib_decoded.eq(0x18), 7:cur_nib_decoded.eq(0x10), 8:cur_nib_decoded.eq(0x18), 9:cur_nib_decoded.eq(0x38), 10:cur_nib_decoded.eq(0x24), 11:cur_nib_decoded.eq(0x38), 12:cur_nib_decoded.eq(0x18), 13:cur_nib_decoded.eq(0x38), 14:cur_nib_decoded.eq(0x3c), 15:cur_nib_decoded.eq(0x20),
}),
}
),
]

106
Zled.py Normal file
View File

@@ -0,0 +1,106 @@
from migen import *
from migen.genlib.fifo import *
from litex.soc.interconnect.csr import *
class Zled(Module, AutoCSR):
def __init__(self, platform):
self.data0 = data0 = CSRStorage(32, description = "data 0")
self.data1 = data1 = CSRStorage(32, description = "data 1")
ctr_hundredth = Signal(20)
ctr_count = Signal(7)
leds = platform.request_all("user_led")
self.submodules.fsm = fsm = FSM(reset_state="Reset")
self.sync += [
If(ctr_hundredth != 0,
ctr_hundredth.eq(ctr_hundredth - 1),
).Else(
ctr_hundredth.eq(1000000),
If(ctr_count != 0,
ctr_count.eq(ctr_count - 1),
),
)
]
display_idx = Signal(5, reset = 0x1F)
self.comb += [
Case(display_idx, {
0x00: [ leds.eq(data0.storage[ 0: 8]), ],
0x01: [ leds.eq(data0.storage[ 8:16]), ],
0x02: [ leds.eq(data0.storage[16:24]), ],
0x03: [ leds.eq(data0.storage[24:32]), ],
0x04: [ leds.eq(data1.storage[ 0: 8]), ],
0x05: [ leds.eq(data1.storage[ 8:16]), ],
0x06: [ leds.eq(data1.storage[16:24]), ],
0x07: [ leds.eq(data1.storage[24:32]), ],
0x08: [ leds.eq(0x00), ],
0x10: [ leds.eq(0x01), ],
0x11: [ leds.eq(0x02), ],
0x12: [ leds.eq(0x04), ],
0x13: [ leds.eq(0x08), ],
0x14: [ leds.eq(0x10), ],
0x15: [ leds.eq(0x20), ],
0x16: [ leds.eq(0x40), ],
0x17: [ leds.eq(0x80), ],
0x18: [ leds.eq(0xFF), ],
"default": [ leds.eq(0x00), ],
}),
]
bytenum = Signal(3)
flashes = Signal(4)
fsm.act("Reset",
NextValue(ctr_count, 12),
NextValue(display_idx, 0x10),
NextState("March"),
)
###################
# must arrive with ctr_count == 12, display_idx == 0x10
fsm.act("March",
If((ctr_hundredth == 0) & (ctr_count == 0),
If(display_idx == 0x17, # finished
NextValue(ctr_count, 100),
NextValue(display_idx, 0x00),
NextValue(bytenum, 0),
NextState("Byte"),
).Else(
NextValue(display_idx, display_idx + 1),
NextValue(ctr_count, 12),
)
),
)
###################
fsm.act("Byte",
If((ctr_hundredth == 0) & (ctr_count == 0),
If(bytenum == 0x7,
NextValue(ctr_count, 12),
NextValue(display_idx, 0x10),
NextState("March"),
).Else(
NextValue(bytenum, bytenum + 1),
NextValue(ctr_count, 5),
NextValue(display_idx, 0x08),
NextValue(flashes, 5),
NextState("Flash"),
)
),
)
###################
fsm.act("Flash",
If((ctr_hundredth == 0) & (ctr_count == 0),
If(flashes == 0, # finished
NextValue(ctr_count, 100),
NextValue(display_idx, Cat(bytenum, Signal(len(display_idx) - len(bytenum), reset = 0))),
NextState("Byte"),
).Else(
NextValue(display_idx, display_idx ^ 0x10),
NextValue(flashes, flashes - 1),
NextValue(ctr_count, 5),
)
),
)

162
Zscreen.py Normal file
View File

@@ -0,0 +1,162 @@
from migen import *
from migen.genlib.fifo import *
from litex.soc.interconnect.csr import *
class Zscreen(Module, AutoCSR):
def __init__(self, platform, wb, width=1920, height=1080, depth=8):
self.trace_data = trace_data = CSRStorage(32, description = "trace_data")
self.submodules.fifo = fifo = SyncFIFOBuffered(width=32,depth=128)
self.comb += [
fifo.din.eq(trace_data.storage),
fifo.we.eq(trace_data.re),
]
#curx = Signal(12, reset = (width-136))
#cury = Signal(12, reset = (8))
default_address = (0x8f800000 + (width-138) + (width*24) ) // 4 # FIXME: for 8 MiB FB only IMPROVEME: assumes 32-bits WB
last_address = (0x8f800000 + (width-138) + (width*1072)) // 4 # FIXME: for 8 MiB FB only IMPROVEME: assumes 32-bits WB
address_increment = (width) // 4
#default_address = (0x8f800000 + width//2 + width*height//2) // 4
#last_address = (0x8f800000 + width//2 + width*(height-8)) // 4
#address_increment = (width) // 4
base_address = Signal(30, reset = default_address) # IMPROVEME: assumes 32-bits WB
cur_address = Signal(30)
print(f"TRACE: displaying @ 0x{default_address*4:08x} with increment {address_increment*4}")
cur_nib_idx = Signal(3)
cur_line_idx = Signal(3)
val = Signal(32)
cur_nib = Signal(4)
self.comb += [
Case((cur_nib_idx ^ 1), { ## nibble-swap for proper order
x: cur_nib.eq(val[(4*x):(4*x+4)]) for x in range(0, 8)
}),
]
cur_nib_decoded = Signal(8)
self.submodules.fsm = fsm = FSM(reset_state="Reset")
saw_ongoing = Signal()
saw_re = Signal()
saw_readable= Signal()
self.sync += [
If(~fsm.ongoing("Idle"),
saw_ongoing.eq(1),
),
If(trace_data.re,
saw_re.eq(1),
),
If(fifo.readable,
saw_readable.eq(1),
),
]
if (False):
led0 = platform.request("user_led", 0)
led1 = platform.request("user_led", 1)
led2 = platform.request("user_led", 2)
led3 = platform.request("user_led", 3)
led4 = platform.request("user_led", 4)
led5 = platform.request("user_led", 5)
led6 = platform.request("user_led", 6)
led7 = platform.request("user_led", 7)
self.comb += [
led0.eq(~fsm.ongoing("Idle")),
led1.eq(trace_data.re),
led2.eq(fifo.readable),
led3.eq(0),
led4.eq(saw_ongoing),
led5.eq(saw_re),
led6.eq(saw_readable),
led7.eq(trace_data.storage[0]),
]
# set up wishbone
self.comb += [
wb.we.eq(1),
wb.sel.eq(0xF),
wb.adr.eq(cur_address),
]
fsm.act("Reset",
NextState("Idle"),
)
fsm.act("Idle",
If(fifo.readable,
NextValue(val, fifo.dout),
NextValue(cur_line_idx, 0),
NextValue(cur_nib_idx, 0),
NextValue(cur_address, base_address),
fifo.re.eq(1),
NextState("StartWriteLow"),
),
)
fsm.act("StartWriteLow",
wb.cyc.eq(1),
wb.stb.eq(1),
wb.dat_w.eq(Cat(Replicate(cur_nib_decoded[7], 8), Replicate(cur_nib_decoded[6], 8), Replicate(cur_nib_decoded[5], 8), Replicate(cur_nib_decoded[4], 8))),
If(wb.ack,
NextValue(cur_address, cur_address + 1),
NextState("StartWriteHigh"),
),
)
fsm.act("StartWriteHigh",
wb.cyc.eq(1),
wb.stb.eq(1),
wb.dat_w.eq(Cat(Replicate(cur_nib_decoded[3], 8), Replicate(cur_nib_decoded[2], 8), Replicate(cur_nib_decoded[1], 8), Replicate(cur_nib_decoded[0], 8))),
If(wb.ack,
If((cur_line_idx == 7) & (cur_nib_idx == 7),
#If(base_address == last_address,
# NextValue(base_address, default_address),
#).Else(
NextValue(base_address, base_address + address_increment),
#),
NextState("Idle"),
).Elif(cur_nib_idx == 7,
NextValue(base_address, base_address + address_increment),
NextValue(cur_address, base_address + address_increment),
NextValue(cur_nib_idx, 0),
NextValue(cur_line_idx, cur_line_idx + 1),
NextState("StartWriteLow"),
).Else(
NextValue(cur_address, cur_address + 1),
NextValue(cur_nib_idx, cur_nib_idx + 1),
NextState("StartWriteLow"),
)
),
)
self.comb += [
Case((cur_line_idx), {
0: cur_nib_decoded.eq(0),
7: cur_nib_decoded.eq(0),
1: Case(cur_nib, {
0:cur_nib_decoded.eq(0x18), 1:cur_nib_decoded.eq(0x10), 2:cur_nib_decoded.eq(0x18), 3:cur_nib_decoded.eq(0x38), 4:cur_nib_decoded.eq(0x20), 5:cur_nib_decoded.eq(0x3c), 6:cur_nib_decoded.eq(0x1c), 7:cur_nib_decoded.eq(0x3c), 8:cur_nib_decoded.eq(0x18), 9:cur_nib_decoded.eq(0x18), 10:cur_nib_decoded.eq(0x18), 11:cur_nib_decoded.eq(0x38), 12:cur_nib_decoded.eq(0x18), 13:cur_nib_decoded.eq(0x38), 14:cur_nib_decoded.eq(0x3c), 15:cur_nib_decoded.eq(0x3c),
}),
2: Case(cur_nib, {
0:cur_nib_decoded.eq(0x24), 1:cur_nib_decoded.eq(0x30), 2:cur_nib_decoded.eq(0x24), 3:cur_nib_decoded.eq(0x04), 4:cur_nib_decoded.eq(0x28), 5:cur_nib_decoded.eq(0x20), 6:cur_nib_decoded.eq(0x20), 7:cur_nib_decoded.eq(0x04), 8:cur_nib_decoded.eq(0x24), 9:cur_nib_decoded.eq(0x24), 10:cur_nib_decoded.eq(0x24), 11:cur_nib_decoded.eq(0x24), 12:cur_nib_decoded.eq(0x24), 13:cur_nib_decoded.eq(0x24), 14:cur_nib_decoded.eq(0x20), 15:cur_nib_decoded.eq(0x20),
}),
3: Case(cur_nib, {
0:cur_nib_decoded.eq(0x2c), 1:cur_nib_decoded.eq(0x10), 2:cur_nib_decoded.eq(0x04), 3:cur_nib_decoded.eq(0x18), 4:cur_nib_decoded.eq(0x28), 5:cur_nib_decoded.eq(0x38), 6:cur_nib_decoded.eq(0x38), 7:cur_nib_decoded.eq(0x08), 8:cur_nib_decoded.eq(0x18), 9:cur_nib_decoded.eq(0x24), 10:cur_nib_decoded.eq(0x24), 11:cur_nib_decoded.eq(0x38), 12:cur_nib_decoded.eq(0x20), 13:cur_nib_decoded.eq(0x24), 14:cur_nib_decoded.eq(0x38), 15:cur_nib_decoded.eq(0x38),
}),
4: Case(cur_nib, {
0:cur_nib_decoded.eq(0x34), 1:cur_nib_decoded.eq(0x10), 2:cur_nib_decoded.eq(0x18), 3:cur_nib_decoded.eq(0x04), 4:cur_nib_decoded.eq(0x3c), 5:cur_nib_decoded.eq(0x04), 6:cur_nib_decoded.eq(0x24), 7:cur_nib_decoded.eq(0x10), 8:cur_nib_decoded.eq(0x24), 9:cur_nib_decoded.eq(0x1c), 10:cur_nib_decoded.eq(0x3c), 11:cur_nib_decoded.eq(0x24), 12:cur_nib_decoded.eq(0x20), 13:cur_nib_decoded.eq(0x24), 14:cur_nib_decoded.eq(0x20), 15:cur_nib_decoded.eq(0x20),
}),
5: Case(cur_nib, {
0:cur_nib_decoded.eq(0x24), 1:cur_nib_decoded.eq(0x10), 2:cur_nib_decoded.eq(0x20), 3:cur_nib_decoded.eq(0x04), 4:cur_nib_decoded.eq(0x08), 5:cur_nib_decoded.eq(0x04), 6:cur_nib_decoded.eq(0x24), 7:cur_nib_decoded.eq(0x10), 8:cur_nib_decoded.eq(0x24), 9:cur_nib_decoded.eq(0x04), 10:cur_nib_decoded.eq(0x24), 11:cur_nib_decoded.eq(0x24), 12:cur_nib_decoded.eq(0x24), 13:cur_nib_decoded.eq(0x24), 14:cur_nib_decoded.eq(0x20), 15:cur_nib_decoded.eq(0x20),
}),
6: Case(cur_nib, {
0:cur_nib_decoded.eq(0x18), 1:cur_nib_decoded.eq(0x38), 2:cur_nib_decoded.eq(0x3c), 3:cur_nib_decoded.eq(0x38), 4:cur_nib_decoded.eq(0x08), 5:cur_nib_decoded.eq(0x38), 6:cur_nib_decoded.eq(0x18), 7:cur_nib_decoded.eq(0x10), 8:cur_nib_decoded.eq(0x18), 9:cur_nib_decoded.eq(0x38), 10:cur_nib_decoded.eq(0x24), 11:cur_nib_decoded.eq(0x38), 12:cur_nib_decoded.eq(0x18), 13:cur_nib_decoded.eq(0x38), 14:cur_nib_decoded.eq(0x3c), 15:cur_nib_decoded.eq(0x20),
}),
}
),
]