initial commit
This commit is contained in:
commit
a2df2e96e8
107 changed files with 36924 additions and 0 deletions
34
.gitignore
vendored
Normal file
34
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
pcomp/*.s
|
||||
progs/*.s
|
||||
tests/*.s
|
||||
examples/*.s
|
||||
*.o
|
||||
*.exe
|
||||
*.bin
|
||||
*.sym
|
||||
*.swp
|
||||
*.prog
|
||||
*.out
|
||||
*.dis
|
||||
*.sasmout
|
||||
*.lib
|
||||
*.img
|
||||
*.mem
|
||||
*.lsym
|
||||
*.zip
|
||||
go.sum
|
||||
sine.pas
|
||||
graph1.pas
|
||||
graph2.pas
|
||||
chase.pas
|
||||
!runtime.s
|
||||
**/tridoracpu.*/
|
||||
rtl/arty-a7/mig_dram_0/_tmp/*
|
||||
rtl/arty-a7/mig_dram_0/doc/*
|
||||
rtl/arty-a7/mig_dram_0/mig_dram_0*
|
||||
rtl/arty-a7/mig_dram_0/xil_txt.*
|
||||
rtl/arty-a7/mig_dram_0/*.veo
|
||||
rtl/arty-a7/mig_dram_0/*.tcl
|
||||
rtl/arty-a7/mig_dram_0/*.xml
|
||||
rtl/arty-a7/mig_dram_0/*.v
|
||||
rtl/arty-a7/mig_dram_0/*.vhdl
|
||||
17
LICENSE.md
Normal file
17
LICENSE.md
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# Copyright and Licensing
|
||||
|
||||
All files, except where explicitly stated otherwise, are licensed according to the BSD-3-Clause license as follows:
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
Copyright 2024 Sebastian Lederer
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
61
README.md
Normal file
61
README.md
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
# Tridora System
|
||||
Tridora is a homebrew CPU written in Verilog and a matching software environment,
|
||||
including a Pascal compiler and assembler.
|
||||
Everything was created from the ground up (except soldering stuff).
|
||||
Everything is as simple as possible while still being reasonably useful.
|
||||
Everything is open source, so you can read, understand and modify the whole system, hardware and software.
|
||||
|
||||
## Overview
|
||||
- Homebrew CPU
|
||||
- Verilog FPGA SoC
|
||||
- 32-bit word-oriented stack machine architecture
|
||||
- running at 83 MHz on an Arty-A7 board with four clocks per instruction
|
||||
- has its own instruction set architecture, compatible with nothing
|
||||
- additional IO controllers on FPGA: UART (serial console), SD-Card, VGA
|
||||
- Pascal compiler written from zero
|
||||
- CPU and compiler were designed together
|
||||
- minimal operating system
|
||||
- editor, compiler, assembler run natively
|
||||
- so you can develop programs directly on the machine
|
||||
- small: CPU has 760 lines of verilog, compiler ~9000 LoC
|
||||
- Compiler written in Pascal and can compile itself
|
||||
- Cross-compiler/-assembler can be compiled with FPC
|
||||
- Compiler does its own Pascal dialect with some restrictions and some extensions
|
||||
- Emulator available
|
||||
|
||||
## Demo
|
||||
- (Video hello world)
|
||||
- (Video lines)
|
||||
- (Screenshot mandelbrot)
|
||||
- (Screenshot conway)
|
||||
- (Screenshot image viewer)
|
||||
|
||||
## Supported Boards
|
||||
- Arty A7 (with two PMODs for microSD cards and VGA output)
|
||||
- Nexys A7 (not ready yet)
|
||||
|
||||
## Pascal Language
|
||||
- Wirth Pascal
|
||||
- no function types/parameters
|
||||
- arbitrary length strings (2GB)
|
||||
- safe strings (runtime information about max/current size)
|
||||
- tiny sets (machine word sized), that means no SET OF CHAR
|
||||
- array literals with IN-operator, which can replace most uses of SET OF CHAR
|
||||
- nested procedures with some limitations
|
||||
- 32 bit software floating point with low precision (5-6 digits)
|
||||
- break and exit statements, no continue yet
|
||||
- static variable initialization for global variables
|
||||
- non-standard file i/o (because the standard sucks, obl. XKCD reference)
|
||||
|
||||
## Standard Library
|
||||
- everything from Wirth Pascal
|
||||
- some things from TP3.0
|
||||
- some graphics functionality (to be expanded in the future)
|
||||
|
||||
## Operating System
|
||||
- not a real operating system, more of a program loader
|
||||
- some assembly routines for I/O resident in memory
|
||||
- one program image loaded at a time at a fixed address
|
||||
- most parts of the operating system are contained in the program image
|
||||
- file system is very primitive: only contiguous blocks, no subdirectories
|
||||
- simple shell reminiscent of TP3.0, edit, compile, run programs
|
||||
48
doc/irqctrl.md
Normal file
48
doc/irqctrl.md
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
# Interrupt Controller
|
||||
The interrupt controller uses a single register at address: $980
|
||||
|
||||
## Reading the status register
|
||||
|
||||
|_bit_ |31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16|
|
||||
|- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |
|
||||
|_Value_|t |t |t |t |t |t |t |t |t |t|t |t |t |t |t |t |
|
||||
|
||||
|_bit_ |15|14|13|12|11|10|09|08|07|06|05|04|03|02|01|00|
|
||||
|- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |
|
||||
|_Value_|t |t |t |t |t |t |t |t |- |- |- |- |- |- |p1 |p0 |
|
||||
|
||||
|
||||
|Bitfields|Description|
|
||||
|---------|-----------|
|
||||
| _t_ | unsigned 24 bit counter of timer ticks since reset
|
||||
| _p1_ | IRQ 1 (timer tick) interrupt pending if 1
|
||||
| _p0_ | IRQ 0 (UART) interrupt pending if 1
|
||||
|
||||
|
||||
|
||||
## Writing the status register
|
||||
|
||||
|_bit_ |31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16|
|
||||
|- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |
|
||||
|_Value_|- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |
|
||||
|
||||
|_bit_ |15|14|13|12|11|10|09|08|07|06|05|04|03|02|01|00|
|
||||
|- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |
|
||||
|_Value_|- |- |- |- |- |- |- |- |i |- |- |- |- |- |- |- |
|
||||
|
||||
|
||||
|Bitfields|Description|
|
||||
|---------|-----------|
|
||||
| _i_ | 1 = interrupts enabled, 0 = interrupts disabled
|
||||
|
||||
|
||||
## Notes
|
||||
Interrupt processing is disabled on reset and needs to be enabled by writing a value with
|
||||
bit 7 set to the status register (i.e. 127).
|
||||
|
||||
An interrupt is only signaled once to the CPU whenever one of the IRQ signals becomes active.
|
||||
|
||||
Reading the status register will reflect all pending interrupts since enabling interrupt processing.
|
||||
|
||||
Interrupt processing needs to be re-enabled after an interrupt occurs by setting bit 7 in the status register again. This will also clear all pending interrupts.
|
||||
|
||||
37
doc/mem.md
Normal file
37
doc/mem.md
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
# Memory Layout
|
||||
|
||||
The Tridora system uses the following memory layout:
|
||||
|
||||
|Address (hex) |Address (decimal)|Description|
|
||||
|-------|-----------|------------------------|
|
||||
|$00000000| 0 | ROM |
|
||||
|$00000800| 2048 | I/O area|
|
||||
|$00001000| 4096 | RAM (SRAM)|
|
||||
|$00010000| 65536 | RAM (DRAM)|
|
||||
|
||||
## Accessing Words and Bytes
|
||||
Memory is word-oriented, so there is no access to individual bytes. Memory transfers always use 32 bits.
|
||||
Word addresses in RAM and ROM use an increment of 4, so the first memory word is at address 0, the second is at address 4 etc.
|
||||
|
||||
This way, you can express a pointer to a specific byte within a word.
|
||||
The lower two bits of the address are ignored when accessing RAM or ROM. So if you use 1 as a memory address, you still get the memory word at address 0.
|
||||
The lower two bits of the address can be viewed as a byte address (0-3) within the word.
|
||||
|
||||
The _BSEL_ and _BPLC_ instructions are designed to assist with accessing bytes within a word.
|
||||
|
||||
Because memory is always accessed in words, the CPU is neither big-endian nor little-endian. However, the _BSEL_ and _BPLC_
|
||||
instructions are big-endian when accessing bytes within a word, so the system can be considered big-endian.
|
||||
|
||||
## Accessing the I/O Area
|
||||
The I/O area organizes memory slightly different. Here, pointing out individual bytes is not very useful, so the I/O controllers use register addresses with increments of one. In practice, there is only the VGA framebuffer controller which uses multiple registers.
|
||||
|
||||
The individual I/O controllers each have a memory area of 128 bytes, so there is a maximum number of 16 I/O controllers.
|
||||
|
||||
Currently, only I/O slots 0-3 are being used.
|
||||
|
||||
|I/O slot| Address | Controller |
|
||||
|--------|---------|------------|
|
||||
| 0 | $800 | UART |
|
||||
| 1 | $880 | VGA |
|
||||
| 2 | $900 | SPI-SD |
|
||||
| 3 | $980 | IRQC |
|
||||
67
doc/spisd.md
Normal file
67
doc/spisd.md
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
# SPI SD-Card Controller
|
||||
The SPI-SD-Card controller uses a single register at address $880.
|
||||
|
||||
## Reading the register
|
||||
|_bit_ |31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16|
|
||||
|- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |
|
||||
|_Value_|- |- |- |- |- |- |- |- |- |-|- |- |- |- |- |- |
|
||||
|
||||
|_bit_ |15|14|13|12|11|10|09|08|07|06|05|04|03|02|01|00|
|
||||
|- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |
|
||||
|_Value_|- |cd |cc |cb |tr |te |ra |ro |d |d |d |d |d |d |d |d |
|
||||
|
||||
|
||||
|Bitfields|Description|
|
||||
|---------|-----------|
|
||||
| _cd_ | card detect |
|
||||
| _cc_ | card changed |
|
||||
| _cb_ | card busy |
|
||||
| _tr_ | transmitter ready |
|
||||
| _te_ | transmitter fifo empty |
|
||||
| _ra_ | received byte available |
|
||||
| _ro_ | receiver overrun |
|
||||
| _d_ | received byte data |
|
||||
|
||||
Reading the register does not advance to the next byte in the read fifo. This is done by using the DR bit on a register write (see below).
|
||||
|
||||
## Writing the register
|
||||
|
||||
|_bit_ |31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16|
|
||||
|- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |
|
||||
|_Value_|- |- |- |- |- |- |- |- |- |-|- |- |- |- |- |- |
|
||||
|
||||
|_bit_ |15|14|13|12|11|10|09|08|07|06|05|04|03|02|01|00|
|
||||
|- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |
|
||||
|_Value_|- |CW |CF |Cx |Cc |Cd |DR |DW |D |D |D |D |D |D |D |D |
|
||||
|
||||
|
||||
|Bitfields|Description|
|
||||
|---------|-----------|
|
||||
| _CW_ | control write |
|
||||
| _CF_ | enable receive filter |
|
||||
| _Cx_ | enable transceiver |
|
||||
| _Cc_ | force spi clock on |
|
||||
| _Cd_ | write clock divider |
|
||||
| _DR_ | read acknowledge |
|
||||
| _DW_ | data write |
|
||||
| _D_ | byte data |
|
||||
|
||||
* CF, Cx and Cc flags are used together with CW
|
||||
* Cd together with d sets the clock divider
|
||||
* DW together with d writes a data byte
|
||||
* if the receive filter is set, all received bytes are ignored until a byte is received that is not $FF
|
||||
* receiving a byte that is not $FF disables the receive filter
|
||||
* Cc is used to enable the clock without sending/receiving anything - used for card initialization
|
||||
|
||||
Example transaction:
|
||||
|
||||
1. read register, loop until _te_ is set
|
||||
1. write command bytes to register (_DW_ | data)
|
||||
1. write _Cx_|_CF_ to register
|
||||
1. read register, loop until _ra_ is set
|
||||
1. process data byte
|
||||
1. write _DR_ to register
|
||||
1. repeat last three steps until complete response has been read
|
||||
1. wait a bit/send a few more $FF bytes
|
||||
1. disable transceiver, write _CW_ to register (Cx = 0)
|
||||
|
||||
1300
doc/tridoracpu.md
Normal file
1300
doc/tridoracpu.md
Normal file
File diff suppressed because it is too large
Load diff
83
doc/vga.md
Normal file
83
doc/vga.md
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
# VGA Controller
|
||||
|
||||
Registers
|
||||
|Name|Address|Description|
|
||||
|----|-------|-----------|
|
||||
|_FB_RA_ | $900 | Read Address |
|
||||
|_FB_WA_ | $901 | Write Address |
|
||||
| _FB_IO_ | $902 | I/O Register |
|
||||
| _FB_PS_ | $903 | Palette Select |
|
||||
| _FB_PD_ | $904 | Palette Data |
|
||||
| _FB_CTL_ | $905 | Control Register |
|
||||
|
||||
|
||||
|
||||
## Pixel Data
|
||||
Pixel data is organized in 32-bit-words. With four bits per pixel, one word
|
||||
contains eight pixels.
|
||||
|
||||
|_bit_ |31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16|
|
||||
|- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |
|
||||
|_Value_|p0 | p0 | p0 | p0 | p1 | p1 | p1 | p1 | p2 | p2 | p2 | p2 | p3 | p3 | p3 | p3 |
|
||||
|
||||
|_bit_ |15|14|13|12|11|10|09|08|07|06|05|04|03|02|01|00|
|
||||
|- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |
|
||||
|_Value_|p4 | p4 | p4 | p4 | p5 | p5 | p5 | p5 | p6 | 62 | p6 | p6 | p7 | p7 | p7 | p7 |
|
||||
|
||||
|Bitfields|Description|
|
||||
|---------|-----------|
|
||||
| _p0_ | 4 bits color value (leftmost pixel) |
|
||||
| _p1_ | 4 bits color value |
|
||||
| _p2_ | 4 bits color value |
|
||||
| _p3_ | 4 bits color value |
|
||||
| _p4_ | 4 bits color value |
|
||||
| _p5_ | 4 bits color value |
|
||||
| _p6_ | 4 bits color value |
|
||||
| _p7_ | 4 bits color value (rightmost pixel) |
|
||||
|
||||
Video memory uses a linear layout, with words using an address increment of one.
|
||||
The first word (horizontal pixel coordinates 0-3) is at address 0, the second (coordinates 4-7) at address 1 etc.
|
||||
The first line starts at address 0, the second at address 80 etc.
|
||||
|
||||
To access video memory, the corresponding video memory address must be written to a latch register, then pixel data can be read or written by the I/O register. Reading and writing uses separate latch registers (the "Read Adress" and "Write Address" registers. To read the same word and write it back, both addresses need to be set.
|
||||
Both registers have an auto-increment function. After reading the I/O register, the FB_RA register is ingremented by one. After writing to the I/O register, the FB_WA register is incremented by one.
|
||||
|
||||
## Palette Data
|
||||
The VGA controller uses a 16 color palette. The palette can be changed with the FB_PS and FB_PD registers. Writing to the FB_PS register selects a palette slot. Valid values are 0-15. After a palette slot is selected, color data can be read from and written to the FB_PD register. Color data is organized as follows:
|
||||
|
||||
|_bit_ |31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16|
|
||||
|- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |
|
||||
|_Value_|- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |
|
||||
|
||||
|_bit_ |15|14|13|12|11|10|09|08|07|06|05|04|03|02|01|00|
|
||||
|- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |
|
||||
|_Value_|- |- |- |- |r |r |r |r |g |g |g |g |b |b |b |b |
|
||||
|
||||
| _Bitfields_| Description |
|
||||
|------------|--------------|
|
||||
| _r_ | 4 bits red intensity |
|
||||
| _g_ | 4 bits green intensity |
|
||||
| _b_ | 4 bits blue intensity |
|
||||
|
||||
The FB_PS and PB_FD registers cannot be read.
|
||||
|
||||
## Control Register
|
||||
The control register contains status information. It can only be read.
|
||||
|
||||
|_bit_ |31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16|
|
||||
|- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |
|
||||
|_Value_|m |m |m |m |- |- |- |- |- |- |- |- |- |- |- |- |
|
||||
|
||||
|_bit_ |15|14|13|12|11|10|09|08|07|06|05|04|03|02|01|00|
|
||||
|- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |
|
||||
|_Value_|- |- |- |- |- |- |- |- |- |- |- |- |- |- |- |vb |
|
||||
|
||||
|
||||
| _Bitfields_| Description |
|
||||
|------------|--------------|
|
||||
| _m_ | 4 bits mode indicator |
|
||||
| _vb_ | vertical blank |
|
||||
|
||||
The _m_ field indicates the current graphics mode. At the time of writing, it is
|
||||
always 1 which denotes a 640x400x4 mode.
|
||||
The _vb_ bit is 1 when the video signal generator is in its vertical blank phase.
|
||||
81
examples/3dcube.pas
Normal file
81
examples/3dcube.pas
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
program cube;
|
||||
const Cw = 640;
|
||||
Ch = 400;
|
||||
Vw = 4;
|
||||
Vh = 3;
|
||||
D = 3.5;
|
||||
|
||||
RED = 2;
|
||||
GREEN = 3;
|
||||
BLUE = 4;
|
||||
|
||||
type point2d = record x,y:real; end;
|
||||
point3d = record x,y,z:real; end;
|
||||
|
||||
var vAf,vBf,vCf,vDf:point3d;
|
||||
vAb,vBb,vCb,vDb:point3d;
|
||||
|
||||
function viewportToCanvas(x,y:real):point2d;
|
||||
begin
|
||||
viewportToCanvas.x := x * Cw/Vw;
|
||||
viewportToCanvas.y := y * Ch/Vh;
|
||||
end;
|
||||
|
||||
function projectVertex(v:point3d):point2d;
|
||||
begin
|
||||
projectVertex := viewportToCanvas(v.x * D / v.z, v.y * D / v.z);
|
||||
end;
|
||||
|
||||
procedure initPoint3d(var p:point3d;x,y,z:real);
|
||||
begin
|
||||
p.x := x;
|
||||
p.y := y;
|
||||
p.z := z;
|
||||
end;
|
||||
|
||||
procedure DrawLine2d(p1,p2:point2d; color:integer);
|
||||
begin
|
||||
drawline(Cw div 2 + trunc(p1.x),Ch div 2 + trunc(p1.y),
|
||||
Cw div 2 + trunc(p2.x), Ch div 2 + trunc(p2.y),
|
||||
color);
|
||||
end;
|
||||
|
||||
begin
|
||||
initGraphics;
|
||||
|
||||
(* The four "front" vertices *)
|
||||
initPoint3d(vAf,-2.0, -0.5, 5.0);
|
||||
initPoint3d(vBf,-2.0, 0.5, 5.0);
|
||||
initPoint3d(vCf,-1.0, 0.5, 5.0);
|
||||
initPoint3d(vDf,-1.0, -0.5, 5.0);
|
||||
|
||||
(* The four "back" vertices *)
|
||||
(*
|
||||
vAb = [-2, -0.5, 6]
|
||||
vBb = [-2, 0.5, 6]
|
||||
vCb = [-1, 0.5, 6]
|
||||
vDb = [-1, -0.5, 6] *)
|
||||
|
||||
initPoint3d(vAb,-2.0, -0.5, 6.0);
|
||||
initPoint3d(vBb,-2.0, 0.5, 6.0);
|
||||
initPoint3d(vCb,-1.0, 0.5, 6.0);
|
||||
initPoint3d(vDb,-1.0, -0.5, 6.0);
|
||||
|
||||
(* The front face *)
|
||||
DrawLine2d(ProjectVertex(vAf), ProjectVertex(vBf), BLUE);
|
||||
DrawLine2d(ProjectVertex(vBf), ProjectVertex(vCf), BLUE);
|
||||
DrawLine2d(ProjectVertex(vCf), ProjectVertex(vDf), BLUE);
|
||||
DrawLine2d(ProjectVertex(vDf), ProjectVertex(vAf), BLUE);
|
||||
|
||||
(* The back face *)
|
||||
DrawLine2d(ProjectVertex(vAb), ProjectVertex(vBb), RED);
|
||||
DrawLine2d(ProjectVertex(vBb), ProjectVertex(vCb), RED);
|
||||
DrawLine2d(ProjectVertex(vCb), ProjectVertex(vDb), RED);
|
||||
DrawLine2d(ProjectVertex(vDb), ProjectVertex(vAb), RED);
|
||||
|
||||
(* The front-to-back edges *)
|
||||
DrawLine2d(ProjectVertex(vAf), ProjectVertex(vAb), GREEN);
|
||||
DrawLine2d(ProjectVertex(vBf), ProjectVertex(vBb), GREEN);
|
||||
DrawLine2d(ProjectVertex(vCf), ProjectVertex(vCb), GREEN);
|
||||
DrawLine2d(ProjectVertex(vDf), ProjectVertex(vDb), GREEN);
|
||||
end.
|
||||
10
examples/LICENSES.md
Normal file
10
examples/LICENSES.md
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# rtpair.pas
|
||||
originally from [https://github.com/Postrediori/Pascal-Raytracer](https://github.com/Postrediori/Pascal-Raytracer), no license specified there
|
||||
# Attributions for included media files
|
||||
* ara.pict: Tuxyso / Wikimedia Commons / CC-BY-SA-3.0
|
||||
https://commons.wikimedia.org/wiki/File:Ara-Zoo-Muenster-2013.jpg
|
||||
* snow_leopard.pict: Tambako The Jaguar, CC BY-SA 2.0 <https://creativecommons.org/licenses/by-sa/2.0>, via Wikimedia Commons
|
||||
https://commons.wikimedia.org/wiki/File:Snow_leopard_portrait.jpg
|
||||
* shinkansen.pict: 投稿者が撮影, CC BY-SA 3.0 <http://creativecommons.org/licenses/by-sa/3.0/>, via Wikimedia Commons
|
||||
https://commons.wikimedia.org/wiki/File:0key22-86.JPG
|
||||
|
||||
BIN
examples/ara.pict
Normal file
BIN
examples/ara.pict
Normal file
Binary file not shown.
116
examples/conway.pas
Normal file
116
examples/conway.pas
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
program conway;
|
||||
const cellwidth = 4;
|
||||
cellheight = 4;
|
||||
cols = 40;
|
||||
rows = 25;
|
||||
|
||||
WHITE = 1;
|
||||
BLACK = 0;
|
||||
|
||||
type gridType = array [1..rows, 1..cols] of integer;
|
||||
|
||||
var grid:gridType;
|
||||
ch:char;
|
||||
|
||||
procedure initGrid(var g:gridType);
|
||||
var x,y:integer;
|
||||
begin
|
||||
randomize;
|
||||
for y := 1 to rows do
|
||||
for x := 1 to cols do
|
||||
if (random and 1024) > 512 then
|
||||
grid[y,x] := 1;
|
||||
end;
|
||||
|
||||
procedure updateGrid;
|
||||
var oldGrid:gridType;
|
||||
neighbors:integer;
|
||||
x,y:integer;
|
||||
wasAlive:boolean;
|
||||
isAlive:boolean;
|
||||
gen:integer;
|
||||
begin
|
||||
oldGrid := grid;
|
||||
for y := 1 to rows do
|
||||
for x := 1 to cols do
|
||||
begin
|
||||
wasAlive := oldGrid[y,x] > 0;
|
||||
isAlive := false;
|
||||
|
||||
neighbors := 0;
|
||||
|
||||
if y > 1 then
|
||||
begin
|
||||
if x > 1 then
|
||||
if oldGrid[y-1,x-1] > 0 then neighbors := neighbors + 1;
|
||||
|
||||
if oldGrid[y-1,x] > 0 then neighbors := neighbors + 1;
|
||||
|
||||
if x < cols then
|
||||
if oldGrid[y-1,x+1] > 0 then neighbors := neighbors + 1;
|
||||
end;
|
||||
|
||||
if x > 1 then
|
||||
if oldGrid[y,x-1] > 0 then neighbors := neighbors + 1;
|
||||
if x < cols then
|
||||
if oldGrid[y,x+1] > 0 then neighbors := neighbors + 1;
|
||||
|
||||
if y < rows then
|
||||
begin
|
||||
if x > 1 then
|
||||
if oldGrid[y+1,x-1] > 0 then neighbors := neighbors + 1;
|
||||
|
||||
if oldGrid[y+1,x] > 0 then neighbors := neighbors + 1;
|
||||
|
||||
if x < cols then
|
||||
if oldGrid[y+1,x+1] > 0 then neighbors := neighbors + 1;
|
||||
end;
|
||||
|
||||
if wasAlive then
|
||||
begin
|
||||
if (neighbors = 2) or (neighbors = 3) then
|
||||
isAlive := true;
|
||||
end
|
||||
else
|
||||
if neighbors = 3 then
|
||||
isAlive := true;
|
||||
if isAlive then
|
||||
begin
|
||||
gen := grid[y,x];
|
||||
if gen < 8 then gen := gen + 1;
|
||||
grid[y,x] := gen;
|
||||
end
|
||||
else
|
||||
grid[y,x] := 0;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure drawGrid;
|
||||
var x,y:integer;
|
||||
color:integer;
|
||||
screenx,screeny:integer;
|
||||
begin
|
||||
for x := 1 to cols do
|
||||
for y := 1 to rows do
|
||||
begin
|
||||
color := grid[y,x];
|
||||
|
||||
screenx := x * cellwidth;
|
||||
screeny := y * cellheight;
|
||||
putpixel(screenx,screeny,color);
|
||||
putpixel(screenx+1,screeny,color);
|
||||
putpixel(screenx,screeny+1,color);
|
||||
putpixel(screenx+1,screeny+1,color);
|
||||
end;
|
||||
end;
|
||||
|
||||
begin
|
||||
initGraphics;
|
||||
initGrid(grid);
|
||||
repeat
|
||||
drawGrid;
|
||||
updateGrid;
|
||||
{ delay(100); }
|
||||
until conavail;
|
||||
read(ch);
|
||||
end.
|
||||
15
examples/hellop.pas
Normal file
15
examples/hellop.pas
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
(* a simple test program to say
|
||||
hello to the world *)
|
||||
|
||||
program hello;
|
||||
begin
|
||||
(* if there is an argument, use it *)
|
||||
if ParamCount > 0 then
|
||||
writeln('Hello ', ParamStr(1))
|
||||
else
|
||||
writeln('Hello World!');
|
||||
end.
|
||||
{ Note that the last END needs to be followed by the . character,
|
||||
not by a ; character. This is because ; means that there is
|
||||
another statement. It does not mark the end of the statement
|
||||
like in other languages. The . marks the end of the program text. }
|
||||
70
examples/lines.pas
Normal file
70
examples/lines.pas
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
PROGRAM lines;
|
||||
|
||||
PROCEDURE movinglines(max_x, max_y, max_col, ms:INTEGER);
|
||||
VAR x1,y1:INTEGER;
|
||||
VAR x2,y2:INTEGER;
|
||||
VAR delta_x1, delta_y1:INTEGER;
|
||||
VAR delta_x2, delta_y2:INTEGER;
|
||||
VAR col:INTEGER;
|
||||
BEGIN
|
||||
|
||||
x1 := 120;
|
||||
y1 := 90;
|
||||
|
||||
x2 := 340;
|
||||
y2 := 220;
|
||||
|
||||
delta_x1 := 9;
|
||||
delta_y1 := 4;
|
||||
|
||||
delta_x2 := 3;
|
||||
delta_y2 := 7;
|
||||
|
||||
col := 1;
|
||||
|
||||
WHILE NOT CONAVAIL DO
|
||||
BEGIN
|
||||
x1 := x1 + delta_x1;
|
||||
y1 := y1 + delta_y1;
|
||||
|
||||
x2 := x2 + delta_x2;
|
||||
y2 := y2 + delta_y2;
|
||||
|
||||
IF (x1 > max_x) OR (x1 < 0) THEN
|
||||
BEGIN
|
||||
delta_x1 := -delta_x1;
|
||||
x1 := x1 + delta_x1;
|
||||
END;
|
||||
|
||||
IF (y1 > max_y) OR (y1 < 0) THEN
|
||||
BEGIN
|
||||
delta_y1 := -delta_y1;
|
||||
y1 := y1 + delta_y1;
|
||||
END;
|
||||
|
||||
IF (x2 > max_x) OR (x2 < 0) THEN
|
||||
BEGIN
|
||||
delta_x2 := -delta_x2;
|
||||
x2 := x2 + delta_x2;
|
||||
END;
|
||||
|
||||
IF (y2 > max_y) OR (y2 < 0) THEN
|
||||
BEGIN
|
||||
delta_y2 := -delta_y2;
|
||||
y2 := y2 + delta_y2;
|
||||
END;
|
||||
|
||||
col := col + 1;
|
||||
|
||||
IF col > max_col THEN col := 1;
|
||||
|
||||
DRAWLINE(x1,y1,x2,y2,col);
|
||||
|
||||
delay(ms);
|
||||
END;
|
||||
END;
|
||||
|
||||
BEGIN
|
||||
initgraphics;
|
||||
movinglines(639,399,15,0);
|
||||
END.
|
||||
63
examples/mandelbrot.pas
Normal file
63
examples/mandelbrot.pas
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
program mandelbrot;
|
||||
|
||||
const width = 459; height = 405;
|
||||
xstart = -2.02; xend = 0.7;
|
||||
ystart = -1.2; yend = 1.2;
|
||||
maxIterations = 25;
|
||||
maxColors = 15;
|
||||
|
||||
var dx,dy:real;
|
||||
col,row:integer;
|
||||
cx,cy:real;
|
||||
iterations:integer;
|
||||
colors:array[0..15] of integer = { ($000, $020, $031, $042,
|
||||
$053, $064, $075, $086,
|
||||
$097, $0A8, $0B9, $0CA,
|
||||
$0DB, $0EC, $0FD, $0FF); }
|
||||
|
||||
($000, $100, $200, $411,
|
||||
$522, $633, $744, $855,
|
||||
$966, $A77, $B88, $C99,
|
||||
$DAA, $EBB, $FCC, $FDD);
|
||||
c:integer;
|
||||
|
||||
function iterate(x,y:real):integer;
|
||||
var zx,zy:real;
|
||||
tmp:real;
|
||||
count:integer;
|
||||
begin
|
||||
zx := 0.0; zy := 0.0; count := 0;
|
||||
|
||||
repeat
|
||||
tmp := zx*zx - zy*zy + x;
|
||||
zy := 2.0*zx*zy + cy;
|
||||
zx := tmp;
|
||||
count := count + 1;
|
||||
until (zx*zx + zy*zy > 4.0) or (count = MaxIterations);
|
||||
|
||||
iterate := count;
|
||||
end;
|
||||
|
||||
begin
|
||||
initgraphics;
|
||||
for c:=0 to 15 do
|
||||
setpalette(c, colors[c]);
|
||||
|
||||
dx := (xend - xstart) / (width - 1);
|
||||
dy := (yend - ystart) / (height - 1);
|
||||
|
||||
for col := 0 to width - 1 do
|
||||
begin
|
||||
cx := xstart + col * dx;
|
||||
for row := 0 to height - 1 do
|
||||
begin
|
||||
cy := yend - row * dy;
|
||||
iterations := iterate(cx, cy);
|
||||
if iterations = MaxIterations then
|
||||
c := 0
|
||||
else
|
||||
c := iterations mod MaxColors + 1;
|
||||
putpixel(col, row, c);
|
||||
end;
|
||||
end;
|
||||
end.
|
||||
110
examples/rtpair.pas
Normal file
110
examples/rtpair.pas
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
{ Raytracer for a scene with a pair of spheres and multiple reflections }
|
||||
program RtPair;
|
||||
const MaxX = 639;
|
||||
MaxY = 399;
|
||||
HalfX = 320;
|
||||
HalfY = 200;
|
||||
|
||||
var
|
||||
gd, gm: Integer;
|
||||
N, M: Integer;
|
||||
X, Y, Z: Real;
|
||||
U, V, W: Real;
|
||||
I, E, F, P, D, T, R, G: Real;
|
||||
stopReflection: Boolean;
|
||||
C: Integer;
|
||||
|
||||
function Sign(x: Real): Real;
|
||||
begin
|
||||
if x>0 then
|
||||
Sign := 1
|
||||
else
|
||||
Sign := -1;
|
||||
end;
|
||||
|
||||
|
||||
begin
|
||||
InitGraphics;
|
||||
SetPalette(0, $000);
|
||||
SetPalette(4, $A00);
|
||||
SetPalette(11, $0FF);
|
||||
SetPalette(15, $FFF);
|
||||
|
||||
for N:=0 to MaxY do
|
||||
for M:=0 to MaxX do
|
||||
begin
|
||||
{ Rays' origin point }
|
||||
X := 0;
|
||||
Y := -0.1;
|
||||
Z := 3;
|
||||
|
||||
U := (M - 318) / HalfX;
|
||||
V := (HalfY - N) / 321.34;
|
||||
|
||||
W := 1 / Sqrt(U*U + V*V + 1);
|
||||
U := U*W;
|
||||
V := V*W;
|
||||
|
||||
{ I is the horizontal direction of ray }
|
||||
{ based on whether it is in left (U<0) or right (U>0) half of the screen }
|
||||
I := Sign(U);
|
||||
G := 1;
|
||||
|
||||
{ Start the reflection cycle. }
|
||||
{ A ray may reflect between one sphere and another multiple times before hitting floor or sky. }
|
||||
repeat
|
||||
stopReflection := True;
|
||||
E := X-I;
|
||||
F := Y-I;
|
||||
P := U*E + V*F - W*Z;
|
||||
D := P*P - E*E - F*F - Z*Z + 1;
|
||||
|
||||
{ If ray reflects from a sphere one more time }
|
||||
if D>0 then
|
||||
begin
|
||||
T := -P - Sqrt(D);
|
||||
if T>0 then
|
||||
begin
|
||||
X := X + T*U;
|
||||
Y := Y + T*V;
|
||||
Z := Z - T*W;
|
||||
E := X - I;
|
||||
F := Y - I;
|
||||
G := Z;
|
||||
P := 2*(U*E + V*F - W*G);
|
||||
U := U - P*E;
|
||||
V := V - P*F;
|
||||
W := W + P*G;
|
||||
|
||||
{ Invert ray's direction and continue the reflection cycle }
|
||||
I := -I;
|
||||
stopReflection := False;
|
||||
end;
|
||||
end;
|
||||
until stopReflection;
|
||||
|
||||
{ If Y<0 (V<0) a ray hits the floor }
|
||||
if V<0 then
|
||||
begin
|
||||
P := (Y+2)/V;
|
||||
{ Select checkers floor with Black (0) and Red (4) tiles }
|
||||
C := (1 And (Round(X - U*P) + Round(Z + W*P))) * 4;
|
||||
end else begin
|
||||
{ If Y>0 (V>0) a ray hits the sky }
|
||||
{ Default sky color is Cyan (11) }
|
||||
C := 11;
|
||||
{ Condition for using color White (15) to create fancy Cyan-White horizon }
|
||||
R := ArcTan(U/W);
|
||||
R := 0.2+0.1*Cos(3*R)*Abs(Sin(5*R));
|
||||
if Abs(G)<0.35 then
|
||||
R := R + 1;
|
||||
if V<R then
|
||||
C := 15;
|
||||
end;
|
||||
|
||||
{ Draw current pixel }
|
||||
PutPixel(M, N, C);
|
||||
end;
|
||||
|
||||
repeat until ConAvail;
|
||||
end.
|
||||
BIN
examples/shinkansen.pict
Normal file
BIN
examples/shinkansen.pict
Normal file
Binary file not shown.
BIN
examples/snow_leopard.pict
Normal file
BIN
examples/snow_leopard.pict
Normal file
Binary file not shown.
143
examples/test.txt
Normal file
143
examples/test.txt
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
|
||||
|
||||
THE ELEMENTS OF STYLE
|
||||
|
||||
BY
|
||||
WILLIAM STRUNK, Jr.
|
||||
|
||||
PROFESSOR OF ENGLISH
|
||||
IN
|
||||
CORNELL UNIVERSITY
|
||||
|
||||
NEW YORK
|
||||
HARCOURT, BRACE AND COMPANY
|
||||
|
||||
|
||||
COPYRIGHT, 1918, 1919, BY
|
||||
WILLIAM STRUNK, JR.
|
||||
|
||||
COPYRIGHT, 1920, BY
|
||||
HARCOURT, BRACE AND HOWE, INC.
|
||||
|
||||
|
||||
THE MAPLE PRESS YORK PA
|
||||
|
||||
|
||||
|
||||
|
||||
CONTENTS
|
||||
|
||||
|
||||
Page
|
||||
|
||||
I. Introductory 5
|
||||
|
||||
II. Elementary Rules of Usage 7
|
||||
|
||||
1. Form the possessive singular of nouns by adding _'s_ 7
|
||||
|
||||
2. In a series of three or more terms with a single
|
||||
conjunction, use a comma after each term except the last 7
|
||||
|
||||
3. Enclose parenthetic expressions between commas 8
|
||||
|
||||
4. Place a comma before a conjunction introducing a
|
||||
co-ordinate clause 10
|
||||
|
||||
5. Do not join independent clauses by a comma 11
|
||||
|
||||
6. Do not break sentences in two 12
|
||||
|
||||
7. A participial phrase at the beginning of a sentence must
|
||||
refer to the grammatical subject 13
|
||||
|
||||
III. Elementary Principles of Composition 15
|
||||
|
||||
8. Make the paragraph the unit of composition: one paragraph
|
||||
to each topic 15
|
||||
|
||||
9. As a rule, begin each paragraph with a topic sentence; end
|
||||
it in conformity with the beginning 17
|
||||
|
||||
10. Use the active voice 19
|
||||
|
||||
11. Put statements in positive form 21
|
||||
|
||||
12. Use definite, specific, concrete language 22
|
||||
|
||||
13. Omit needless words 24
|
||||
|
||||
14. Avoid a succession of loose sentences 25
|
||||
|
||||
15. Express co-ordinate ideas in similar form 26
|
||||
|
||||
16. Keep related words together 28
|
||||
|
||||
17. In summaries, keep to one tense 29
|
||||
|
||||
18. Place the emphatic words of a sentence at the end 31
|
||||
|
||||
IV. A Few Matters of Form 33
|
||||
|
||||
V. Words and Expressions Commonly Misused 36
|
||||
|
||||
VI. Spelling 48
|
||||
|
||||
VII. Exercises on Chapters II and III 50
|
||||
|
||||
|
||||
|
||||
|
||||
I. INTRODUCTORY
|
||||
|
||||
|
||||
This book aims to give in brief space the principal requirements of
|
||||
plain English style. It aims to lighten the task of instructor and
|
||||
student by concentrating attention (in Chapters II and III) on a few
|
||||
essentials, the rules of usage and principles of composition most
|
||||
commonly violated. In accordance with this plan it lays down three rules
|
||||
for the use of the comma, instead of a score or more, and one for the
|
||||
use of the semicolon, in the belief that these four rules provide for
|
||||
all the internal punctuation that is required by nineteen sentences out
|
||||
of twenty. Similarly, it gives in Chapter III only those principles of
|
||||
the paragraph and the sentence which are of the widest application. The
|
||||
book thus covers only a small portion of the field of English style. The
|
||||
experience of its writer has been that once past the essentials,
|
||||
students profit most by individual instruction based on the problems of
|
||||
their own work, and that each instructor has his own body of theory,
|
||||
which he may prefer to that offered by any textbook.
|
||||
|
||||
The numbers of the sections may be used as references in correcting
|
||||
manuscript.
|
||||
|
||||
The writer's colleagues in the Department of English in Cornell
|
||||
University have greatly helped him in the preparation of his manuscript.
|
||||
Mr. George McLane Wood has kindly consented to the inclusion under
|
||||
Rule 10 of some material from his _Suggestions to Authors_.
|
||||
|
||||
The following books are recommended for reference or further study: in
|
||||
connection with Chapters II and IV, F. Howard Collins, _Author and
|
||||
Printer_ (Henry Frowde); Chicago University Press, _Manual of Style_;
|
||||
T. L. De Vinne, _Correct Composition_ (The Century Company); Horace
|
||||
Hart, _Rules for Compositors and Printers_ (Oxford University Press);
|
||||
George McLane Wood, _Extracts from the Style-Book of the Government
|
||||
Printing Office_ (United States Geological Survey); in connection with
|
||||
Chapters III and V, _The King's English_ (Oxford University Press); Sir
|
||||
Arthur Quiller-Couch, _The Art of Writing_ (Putnam), especially the
|
||||
chapter, Interlude on Jargon; George McLane Wood, _Suggestions to
|
||||
Authors_ (United States Geological Survey); John Lesslie Hall, _English
|
||||
Usage_ (Scott, Foresman and Co.); James P. Kelley, _Workmanship in
|
||||
Words_ (Little, Brown and Co.). In these will be found full discussions
|
||||
of many points here briefly treated and an abundant store of
|
||||
illustrations to supplement those given in this book.
|
||||
|
||||
It is an old observation that the best writers sometimes disregard the
|
||||
rules of rhetoric. When they do so, however, the reader will usually
|
||||
find in the sentence some compensating merit, attained at the cost of
|
||||
the violation. Unless he is certain of doing as well, he will probably
|
||||
do best to follow the rules. After he has learned, by their guidance, to
|
||||
write plain English adequate for everyday uses, let him look, for the
|
||||
secrets of style, to the study of the masters of literature.
|
||||
|
||||
|
||||
|
||||
44
examples/viewpict.pas
Normal file
44
examples/viewpict.pas
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
program viewpict;
|
||||
type PictData = record
|
||||
magic, mode:integer;
|
||||
palette: array [0..15] of integer;
|
||||
pixeldata: array [0..31999] of integer;
|
||||
end;
|
||||
|
||||
|
||||
var pic:PictData;
|
||||
filename:string;
|
||||
infile:file;
|
||||
ch:char;
|
||||
|
||||
procedure loadPalette(var pic:PictData);
|
||||
var i:integer;
|
||||
begin
|
||||
for i := 0 to 15 do
|
||||
setpalette(i, pic.palette[i]);
|
||||
end;
|
||||
|
||||
procedure loadPic(var pic:PictData);
|
||||
begin
|
||||
PutScreen(pic.pixeldata);
|
||||
end;
|
||||
|
||||
begin
|
||||
if ParamCount > 0 then
|
||||
filename := ParamStr(1)
|
||||
else
|
||||
begin
|
||||
write('Filename> ');
|
||||
readln(filename);
|
||||
end;
|
||||
|
||||
open(infile, filename, ModeReadonly);
|
||||
read(infile, pic);
|
||||
close(infile);
|
||||
|
||||
writeln('magic: ', pic.magic, ' mode:', pic.mode);
|
||||
|
||||
loadPalette(pic);
|
||||
loadPic(pic);
|
||||
read(ch);
|
||||
end.
|
||||
1501
lib/corelib.s
Normal file
1501
lib/corelib.s
Normal file
File diff suppressed because it is too large
Load diff
279
lib/coreloader.s
Normal file
279
lib/coreloader.s
Normal file
|
|
@ -0,0 +1,279 @@
|
|||
; Copyright 2021-2024 Sebastian Lederer. See the file LICENSE.md for details
|
||||
.ORG 4096
|
||||
|
||||
CORELOADER:
|
||||
; initialize program stack and
|
||||
; return stack pointers
|
||||
LOADCP 24060
|
||||
STOREREG FP
|
||||
LOADCP 24064
|
||||
STOREREG RP
|
||||
|
||||
LOADCP SYSBOOTTICKS
|
||||
LOADCP GETTICKS
|
||||
CALL
|
||||
STOREI
|
||||
DROP
|
||||
|
||||
LOADCP INITSDCARD
|
||||
CALL
|
||||
|
||||
;LOADCP FIND_SYSPART ; no need to call, it never
|
||||
;CALL ; returns, so just fall through
|
||||
|
||||
.EQU PART_START 0
|
||||
.EQU EXTENT_SIZE 4
|
||||
.EQU DIR_SIZE 8
|
||||
.EQU SLOT_NO 12
|
||||
.EQU SIZE_BYTES 16
|
||||
.EQU PRG_START_BLK 20
|
||||
.EQU FIND_FS 24
|
||||
|
||||
.EQU PARTENTRY_SIZE 64
|
||||
.EQU DIRENTRY_SIZE 64
|
||||
FIND_SYSPART:
|
||||
FPADJ -FIND_FS
|
||||
; load block 0
|
||||
LOADC 0
|
||||
LOADCP CARDREADBLK
|
||||
CALL
|
||||
|
||||
DUP ; non-zero return code means error
|
||||
CBRANCH.Z FIND_1
|
||||
|
||||
LOADCP PRINTHEXW
|
||||
CALL
|
||||
LOADCP NEWLINE
|
||||
CALL
|
||||
LOADC 0
|
||||
JUMP
|
||||
|
||||
FIND_1:
|
||||
DROP ; remove return code
|
||||
|
||||
;LOADC 512
|
||||
;LOADCP CARD_BUF
|
||||
;LOADCP HEXDUMP
|
||||
;CALL
|
||||
|
||||
; address of the first partition entry
|
||||
LOADCP CARD_BUF
|
||||
FIND_L:
|
||||
DUP ; dup addr for comparison
|
||||
LOADCP SYSPART_NAME
|
||||
LOADC SYSNAME_WORDS
|
||||
LOADCP _CMPWORDS
|
||||
CALL
|
||||
CBRANCH.NZ FIND_FOUND
|
||||
; go to next entry
|
||||
LOADC PARTENTRY_SIZE
|
||||
ADD
|
||||
|
||||
; check if address is still
|
||||
; within the data block
|
||||
DUP
|
||||
LOADCP CARD_BUF,512
|
||||
CMP LT
|
||||
CBRANCH FIND_L
|
||||
|
||||
; remove address
|
||||
DROP
|
||||
|
||||
; not found, complain and
|
||||
; go back to ROM monitor
|
||||
LOADCP SYSPART_ERR
|
||||
LOADCP PRINTLINE
|
||||
CALL
|
||||
|
||||
LOADC 0
|
||||
JUMP
|
||||
|
||||
FIND_FOUND:
|
||||
; address of the part entry is on stack
|
||||
|
||||
; check if partition is enabled
|
||||
DUP ; duplicate address
|
||||
LOADC 40 ; add PartFlags field offset
|
||||
ADD
|
||||
LOADI
|
||||
LOADC 1
|
||||
AND ; check bit 0 (PartEnabled)
|
||||
CBRANCH.Z FIND_L ; if not set, continue loop
|
||||
|
||||
|
||||
; address of part entry is still on stack
|
||||
DUP
|
||||
LOADC 44 ; add startBlock field offset
|
||||
ADD
|
||||
LOADI ; get start block number
|
||||
STORE PART_START
|
||||
|
||||
; address of part entry is still on stack
|
||||
DUP
|
||||
LOADC 52 ; move to extentSize field
|
||||
ADD
|
||||
LOADI ; get value
|
||||
STORE EXTENT_SIZE
|
||||
|
||||
; address of part entry is still on stack
|
||||
LOADC 56 ; move to dirSize field
|
||||
ADD
|
||||
LOADI ; get value
|
||||
STORE DIR_SIZE
|
||||
|
||||
LOADC 0
|
||||
STORE SLOT_NO ; start with dirslot 0
|
||||
|
||||
LOAD PART_START ; start with first block of the partition
|
||||
FIND_FILE:
|
||||
DUP ; duplicate block number
|
||||
LOADCP CARDREADBLK ; read that block
|
||||
CALL
|
||||
DROP ; ignore error
|
||||
|
||||
; scan directory entries for shell file name
|
||||
LOADCP CARD_BUF
|
||||
FIND_FILE_L:
|
||||
DUP
|
||||
LOADCP SHELL_NAME
|
||||
LOADC SHELLNAME_WORDS
|
||||
LOADCP _CMPWORDS ; compare names
|
||||
CALL
|
||||
CBRANCH.NZ FIND_F_FOUND ; exit loop if names match
|
||||
|
||||
; check if current dirslot no
|
||||
; is below maximum number of slots
|
||||
LOAD SLOT_NO
|
||||
LOAD DIR_SIZE
|
||||
CMP GE
|
||||
CBRANCH FIND_F_NOTFOUND ; max slots reached, exit
|
||||
|
||||
; add 1 to SLOT_NO
|
||||
LOAD SLOT_NO
|
||||
INC 1
|
||||
STORE SLOT_NO
|
||||
|
||||
; address is still on stack
|
||||
LOADC DIRENTRY_SIZE
|
||||
ADD ; go to next dir entry
|
||||
|
||||
; check if address is still
|
||||
; below end of data block
|
||||
DUP
|
||||
LOADCP CARD_BUF,512
|
||||
CMP LT
|
||||
CBRANCH FIND_FILE_L ; if it is below, loop
|
||||
|
||||
DROP ; remove dir entry addr
|
||||
|
||||
; block no is still on stack
|
||||
INC 1
|
||||
BRANCH FIND_FILE ; read next block
|
||||
|
||||
FIND_F_NOTFOUND:
|
||||
LOADCP SHELL_ERR
|
||||
LOADCP PRINTLINE
|
||||
CALL
|
||||
|
||||
; remove entry addr and block number
|
||||
DROP
|
||||
DROP
|
||||
LOADC 0
|
||||
JUMP
|
||||
|
||||
FIND_F_FOUND:
|
||||
; found the file name, now check if it has the right flags
|
||||
|
||||
; address of dir entry is still on stack
|
||||
DUP
|
||||
LOADC 40 ; add flags field offset
|
||||
ADD
|
||||
LOADI ; load flags
|
||||
LOADC 16 ; test for SlotFirst flag
|
||||
AND
|
||||
CBRANCH.Z FIND_FILE_L ; if not set, continue loop
|
||||
|
||||
;LOADCP FOUND_MSG
|
||||
;LOADCP PRINTLINE
|
||||
;CALL
|
||||
|
||||
; we got the right file, now calculate start block
|
||||
; and get file size from dir entry
|
||||
|
||||
; address of dir entry is still on stack
|
||||
; phys start block = part start + slot_no * (extent_size/512)
|
||||
LOAD EXTENT_SIZE
|
||||
LOADC 9
|
||||
LOADCP _SHRM
|
||||
CALL
|
||||
LOAD SLOT_NO
|
||||
LOADCP _MUL
|
||||
CALL
|
||||
LOAD PART_START
|
||||
ADD
|
||||
|
||||
;DUP
|
||||
;LOADCP PRINTHEXW
|
||||
;CALL
|
||||
;LOADC ' '
|
||||
;LOADCP CONOUT
|
||||
;CALL
|
||||
|
||||
STORE PRG_START_BLK
|
||||
|
||||
; address of dir entry is still on stack
|
||||
LOADC 44
|
||||
ADD ; add sizeBytes field offset
|
||||
LOADI ; get size in bytes
|
||||
|
||||
;DUP
|
||||
;LOADCP PRINTHEXW
|
||||
;CALL
|
||||
;LOADCP NEWLINE
|
||||
;CALL
|
||||
|
||||
STORE SIZE_BYTES
|
||||
|
||||
; remove block number
|
||||
DROP
|
||||
|
||||
; set argument count to 0
|
||||
; in case this gets called
|
||||
; by a terminating program
|
||||
LOADCP PARGCOUNT
|
||||
LOADC 0
|
||||
STOREI
|
||||
DROP
|
||||
|
||||
LOADC 0 ; device id is always 0
|
||||
LOAD PRG_START_BLK
|
||||
LOAD SIZE_BYTES
|
||||
; release our stack frame
|
||||
FPADJ FIND_FS
|
||||
|
||||
; load program
|
||||
LOADCP CORELOAD
|
||||
CALL
|
||||
|
||||
LOADC 0
|
||||
JUMP
|
||||
|
||||
.CPOOL
|
||||
|
||||
.EQU SYSNAME_WORDS 4
|
||||
SYSPART_NAME:
|
||||
.WORD 6, 32
|
||||
.BYTE "SYSTEM"
|
||||
SYSPART_ERR:
|
||||
.BYTE "No ""SYSTEM"" partition.",13,10,10,0
|
||||
.EQU SHELLNAME_WORDS 5
|
||||
SHELL_NAME:
|
||||
.WORD 10,32
|
||||
.BYTE "shell.prog"
|
||||
SHELL_ERR:
|
||||
.BYTE "No shell on ""SYSTEM"" partition.",13,10,10,0
|
||||
|
||||
FOUND_MSG:
|
||||
.BYTE " shell.prog ",0
|
||||
|
||||
%include corelib.s
|
||||
1053
lib/float32.s
Normal file
1053
lib/float32.s
Normal file
File diff suppressed because it is too large
Load diff
805
lib/rommon.s
Normal file
805
lib/rommon.s
Normal file
|
|
@ -0,0 +1,805 @@
|
|||
.EQU CR 13
|
||||
.EQU LF 10
|
||||
.EQU EOT 4
|
||||
.EQU ACK 6
|
||||
.EQU NAK 21
|
||||
.EQU STX 2
|
||||
.EQU UART_REG 2048
|
||||
.EQU MON_ADDR 64512
|
||||
|
||||
BRANCH 2 ; the very first instruction is not
|
||||
; executed correctly
|
||||
LOADCP 65020 ; initialise FP and RP registers
|
||||
STOREREG FP
|
||||
LOADCP 65024
|
||||
STOREREG RP
|
||||
|
||||
LOADCP MON_ADDR
|
||||
LOADCP 4096
|
||||
STOREI
|
||||
DROP
|
||||
CMDLOOP0:
|
||||
LOADC MESSAGE
|
||||
LOADC PRINTLINE
|
||||
CALL
|
||||
|
||||
CMDLOOP:
|
||||
LOADC NEWLINE
|
||||
CALL
|
||||
LOADC PROMPT
|
||||
CALL
|
||||
CMDLOOP1:
|
||||
LOADC CONIN
|
||||
CALL
|
||||
LOADC TOUPPER
|
||||
CALL
|
||||
|
||||
DUP
|
||||
LOADC CONOUT
|
||||
CALL
|
||||
|
||||
LOADC 'A'
|
||||
CMP.S0 EQ
|
||||
CBRANCH.Z CMD1
|
||||
LOADC CMD_A
|
||||
CALL
|
||||
BRANCH CMDLOOP2
|
||||
CMD1:
|
||||
LOADC 'X'
|
||||
CMP.S0 EQ
|
||||
CBRANCH.Z CMD2
|
||||
LOADC CMD_X
|
||||
CALL
|
||||
BRANCH CMDLOOP2
|
||||
CMD2:
|
||||
LOADC 'D'
|
||||
CMP.S0 EQ
|
||||
CBRANCH.Z CMD3
|
||||
LOADC CMD_D
|
||||
CALL
|
||||
BRANCH CMDLOOP2
|
||||
CMD3:
|
||||
LOADC 'G'
|
||||
CMP.S0 EQ
|
||||
CBRANCH.Z CMD4
|
||||
LOADC CMD_G
|
||||
CALL
|
||||
BRANCH CMDLOOP2
|
||||
CMD4:
|
||||
LOADC 'L'
|
||||
CMP.S0 EQ
|
||||
CBRANCH.Z CMD5
|
||||
LOADC CMD_L
|
||||
CALL
|
||||
BRANCH CMDLOOP2
|
||||
CMD5:
|
||||
LOADC 'B'
|
||||
CMP.S0 EQ
|
||||
CBRANCH.Z CMD6
|
||||
LOADC CMD_B
|
||||
CALL
|
||||
BRANCH CMDLOOP2
|
||||
CMD6:
|
||||
DROP
|
||||
BRANCH CMDLOOP0
|
||||
CMDLOOP2:
|
||||
DROP
|
||||
BRANCH CMDLOOP
|
||||
|
||||
; ---- Command 'A': set current address
|
||||
CMD_A:
|
||||
LOADC 32
|
||||
LOADC CONOUT
|
||||
CALL
|
||||
LOADC READHEX
|
||||
CALL
|
||||
CBRANCH.Z CMD_A_INVALID ; 0 if not valid input
|
||||
LOADCP MON_ADDR
|
||||
SWAP
|
||||
STOREI
|
||||
DROP ; drop STOREI address
|
||||
RET
|
||||
CMD_A_INVALID:
|
||||
DROP
|
||||
LOADC '.'
|
||||
LOADC CONOUT
|
||||
CALL
|
||||
RET
|
||||
|
||||
; ---- Command 'X': examine current address
|
||||
CMD_X:
|
||||
FPADJ -8 ; reserve space for 4 bytes of local variables
|
||||
LOADCP MON_ADDR
|
||||
LOADI
|
||||
STORE 0 ; current memory address
|
||||
LOADC 4 ; print 8 words
|
||||
STORE 4 ; Loop counter
|
||||
CMD_X_LOOP:
|
||||
LOADC 32 ; print a a space
|
||||
LOADC CONOUT
|
||||
CALL
|
||||
LOAD 0 ; load word via current address
|
||||
LOADI
|
||||
LOADC PRINTHEXW ; print it
|
||||
CALL
|
||||
LOAD 0
|
||||
INC 4 ; increment current address
|
||||
STORE 0
|
||||
LOAD 4
|
||||
DEC 1
|
||||
DUP
|
||||
STORE 4
|
||||
CBRANCH.NZ CMD_X_LOOP
|
||||
LOADCP MON_ADDR
|
||||
LOAD 0
|
||||
STOREI
|
||||
DROP
|
||||
FPADJ 8
|
||||
RET
|
||||
|
||||
; ---- Command 'D': deposit words at current address
|
||||
CMD_D:
|
||||
FPADJ -4
|
||||
LOADC 4 ; max number of words
|
||||
STORE 0
|
||||
CMD_D_LOOP:
|
||||
LOADC 32 ; print a space
|
||||
LOADC CONOUT
|
||||
CALL
|
||||
LOADC READHEX
|
||||
CALL
|
||||
DUP
|
||||
CBRANCH.Z CMD_D_EXIT ; check for invalid input
|
||||
SWAP ; swap return code and value
|
||||
LOADCP MON_ADDR
|
||||
LOADI ; get current address
|
||||
SWAP ; swap address and value for STOREI
|
||||
STOREI 4 ; store the value with post-increment of address
|
||||
LOADCP MON_ADDR
|
||||
SWAP ; swap destination address and value for STOREI
|
||||
STOREI ; store the new address
|
||||
DROP
|
||||
LOADC 2 ; compare return code (swapped above) to 2
|
||||
CMP EQ ; check for valid input and return key
|
||||
CBRANCH CMD_D_EXIT
|
||||
LOAD 0
|
||||
DEC 1
|
||||
DUP
|
||||
STORE 0
|
||||
CBRANCH.NZ CMD_D_LOOP
|
||||
CMD_D_EXIT:
|
||||
FPADJ 4
|
||||
RET
|
||||
|
||||
CMD_G:
|
||||
DROP ; remove input char
|
||||
LOADCP NEWLINE
|
||||
CALL
|
||||
LOADCP MON_ADDR
|
||||
LOADI
|
||||
JUMP
|
||||
|
||||
CMD_L:
|
||||
LOADCP NEWLINE
|
||||
CALL
|
||||
LOADCP RCVBLOCKS
|
||||
CALL
|
||||
LOADCP NEWLINE
|
||||
CALL
|
||||
RET
|
||||
|
||||
PROMPT:
|
||||
LOADC '['
|
||||
LOADC CONOUT
|
||||
CALL
|
||||
LOADCP MON_ADDR
|
||||
LOADI
|
||||
LOADC PRINTHEXW
|
||||
CALL
|
||||
LOADC PROMPT2
|
||||
LOADC PRINTLINE
|
||||
CALL
|
||||
RET
|
||||
|
||||
NEWLINE:
|
||||
LOADC CR
|
||||
LOADC CONOUT
|
||||
CALL
|
||||
LOADC LF
|
||||
LOADC CONOUT
|
||||
CALL
|
||||
RET
|
||||
|
||||
; print string of byte characters
|
||||
; takes pointer to string on eval stack
|
||||
PRINTLINE:
|
||||
DUP ; duplicate address as arg to printchar
|
||||
LOADC PRINTCHAR
|
||||
CALL
|
||||
CBRANCH.Z PRINTLINE_EXIT ; if char is zero, exit
|
||||
INC 1 ; increment address
|
||||
BRANCH PRINTLINE
|
||||
PRINTLINE_EXIT:
|
||||
DROP ; remove address from stack
|
||||
RET
|
||||
|
||||
; print a single character
|
||||
; takes a byte pointer on eval stack
|
||||
; returns character on eval stack
|
||||
PRINTCHAR:
|
||||
LOADI.S1.X2Y ; load word, keep address on stack
|
||||
BSEL ; select byte of a word via address
|
||||
DUP ; check for null byte
|
||||
CBRANCH.Z PRINTCHAR_XT
|
||||
DUP
|
||||
LOADC CONOUT
|
||||
CALL
|
||||
PRINTCHAR_XT:
|
||||
RET
|
||||
|
||||
; print a 32-bit hexadecimal number
|
||||
; takes the value on the stack
|
||||
|
||||
PRINTHEXW:
|
||||
BROT
|
||||
DUP
|
||||
LOADC PRINTHEXB
|
||||
CALL
|
||||
BROT
|
||||
DUP
|
||||
LOADC PRINTHEXB
|
||||
CALL
|
||||
BROT
|
||||
DUP
|
||||
LOADC PRINTHEXB
|
||||
CALL
|
||||
BROT
|
||||
LOADC PRINTHEXB
|
||||
CALL
|
||||
RET
|
||||
|
||||
PRINTHEXB:
|
||||
DUP
|
||||
SHR
|
||||
SHR
|
||||
SHR
|
||||
SHR
|
||||
LOADC PRINTNIBBLE
|
||||
CALL
|
||||
LOADC PRINTNIBBLE
|
||||
CALL
|
||||
RET
|
||||
|
||||
PRINTNIBBLE:
|
||||
LOADC 15
|
||||
AND ; isolate nibble
|
||||
LOADC 10
|
||||
CMPU.S0 GE ; nibble >= 10 ?
|
||||
CBRANCH.NZ PRINTNIBBLE_1 ; then print a-f
|
||||
LOADC '0' ; else print 0-9
|
||||
BRANCH PRINTNIBBLE_2
|
||||
PRINTNIBBLE_1:
|
||||
LOADC 55 ; 55 + 10 == 'A'
|
||||
PRINTNIBBLE_2:
|
||||
ADD
|
||||
LOADC CONOUT
|
||||
CALL
|
||||
RET
|
||||
|
||||
; ------ read a 8-digit hexadecimal number from the console
|
||||
; stores variables on the user stack, so the FP register must be
|
||||
; inizialized.
|
||||
; returns two values on the eval stack:
|
||||
; - return code (topmost)
|
||||
; 0 - no valid number
|
||||
; 1 - valid number
|
||||
; 2 - valid number and enter was pressed
|
||||
; - result value
|
||||
|
||||
READHEX:
|
||||
FPADJ -8
|
||||
LOADC 0 ; current value
|
||||
STORE 0
|
||||
LOADC 8 ; max number of digits
|
||||
STORE 4 ; remaining digits counter
|
||||
READHEX_1:
|
||||
LOADC CONIN
|
||||
CALL
|
||||
LOADC CR ; RETURN pressed?
|
||||
CMP.S0 EQ
|
||||
CBRANCH READHEX_RT
|
||||
DUP
|
||||
LOADC CONOUT ; echo character
|
||||
CALL
|
||||
LOADC CONVHEXDIGIT
|
||||
CALL
|
||||
LOADC -1
|
||||
CMP.S0 EQ ; invalid character?
|
||||
CBRANCH.NZ READHEX_XT
|
||||
LOAD 0
|
||||
SHL 2 ; shift previous nibble
|
||||
SHL 2
|
||||
OR ; combine with last digit
|
||||
STORE 0
|
||||
LOAD 4
|
||||
DEC 1
|
||||
DUP
|
||||
STORE 4
|
||||
CBRANCH.NZ READHEX_1
|
||||
BRANCH READHEX_XT1
|
||||
READHEX_RT: ; if no digits were entered, set return code
|
||||
DROP ; drop read character
|
||||
LOAD 4 ;remaining digits counter
|
||||
LOADC 8
|
||||
CMP NE
|
||||
CBRANCH READHEX_RT2
|
||||
LOADC 0 ; no valid input
|
||||
BRANCH READHEX_XT3
|
||||
READHEX_RT2:
|
||||
LOADC 2 ; valid input and return pressed
|
||||
BRANCH READHEX_XT3
|
||||
READHEX_XT:
|
||||
DROP
|
||||
LOAD 4
|
||||
LOADC 8
|
||||
CMP EQ ; if no digits were entered
|
||||
CBRANCH READHEX_XT0
|
||||
READHEX_XT1:
|
||||
LOADC 1 ; valid input flag
|
||||
BRANCH READHEX_XT3
|
||||
READHEX_XT0:
|
||||
LOADC 0
|
||||
READHEX_XT3:
|
||||
LOAD 0
|
||||
SWAP
|
||||
FPADJ 8
|
||||
RET
|
||||
|
||||
; ------ convert character on the eval stack to upper case
|
||||
TOUPPER:
|
||||
LOADC 'a'
|
||||
CMP.S0 LT
|
||||
CBRANCH TOUPPER_XT
|
||||
LOADC 'z'
|
||||
CMP.S0 GT
|
||||
CBRANCH TOUPPER_XT
|
||||
LOADC 32
|
||||
SUB
|
||||
TOUPPER_XT:
|
||||
RET
|
||||
|
||||
; ------ convert hexadecimal digit to integer
|
||||
; ------ takes an ascii character as parameter on the eval stack
|
||||
; ------ returns an integer value from 0-15 on the eval stack,
|
||||
; ------ or -1 if the character was not a valid hexadecimal digit
|
||||
|
||||
CONVHEXDIGIT:
|
||||
LOADC TOUPPER
|
||||
CALL
|
||||
LOADC '0'
|
||||
CMP.S0 LT ; character < '0'?
|
||||
CBRANCH.NZ CONVHEXDIGIT_ERR
|
||||
LOADC '9'
|
||||
CMP.S0 GT ; character > '9'?
|
||||
CBRANCH.NZ CONVHEXDIGIT_ISALPHA
|
||||
LOADC '0' ; character is between '0' and '9', subtract '0'
|
||||
SUB
|
||||
BRANCH CONVHEXDIGIT_NBL
|
||||
CONVHEXDIGIT_ISALPHA:
|
||||
LOADC 'A'
|
||||
CMP.S0 LT ; character < 'A'?
|
||||
CBRANCH.NZ CONVHEXDIGIT_ERR
|
||||
LOADC 'F'
|
||||
CMP.S0 GT ; character > 'F'?
|
||||
CBRANCH.NZ CONVHEXDIGIT_ERR
|
||||
LOADC 55 ; character is between 'A' and 'F', subtract ('A' - 10)
|
||||
SUB
|
||||
CONVHEXDIGIT_NBL:
|
||||
RET
|
||||
CONVHEXDIGIT_ERR:
|
||||
DROP ; remove character from stack
|
||||
LOADC -1 ; error
|
||||
RET
|
||||
|
||||
; --------- output a character on serial console
|
||||
; --------- takes a character (padded to a word) on the eval stack
|
||||
CONOUT:
|
||||
LOADC UART_REG ; address of UART register
|
||||
LOADI ; load status
|
||||
LOADC 256 ; check bit 8 (tx_busy)
|
||||
AND
|
||||
CBRANCH.NZ CONOUT ; loop if bit 8 is not zero
|
||||
|
||||
; transmitter is idle now, write character
|
||||
LOADC UART_REG ; address of UART register
|
||||
SWAP ; swap character and address for STOREI
|
||||
LOADC 1024 ; TX enable bit
|
||||
OR ; OR in the character
|
||||
STOREI
|
||||
DROP
|
||||
RET
|
||||
|
||||
; ---- wait until a character is received and return it on eval stack
|
||||
CONIN:
|
||||
LOADC WAITFORBYTE
|
||||
CALL
|
||||
LOADC -1 ; -1 means timeout
|
||||
CMP.S0 NE
|
||||
CBRANCH CONIN_XT ; exit if no timeout
|
||||
DROP ; remove last result
|
||||
BRANCH CONIN
|
||||
CONIN_XT:
|
||||
RET
|
||||
|
||||
|
||||
.EQU L_BLOCKSIZE 32
|
||||
.EQU L_WORDSIZE 4
|
||||
.EQU CKSUM_PATTERN $AFFECAFE
|
||||
RCVBLOCKS:
|
||||
LOADCP MON_ADDR ; pointer to current write position,
|
||||
LOADI ; kept on stack
|
||||
RCVBLOCKS_L:
|
||||
LOADC WAITFORBYTE ; read header byte
|
||||
CALL
|
||||
LOADC -1
|
||||
CMP.S0 EQ
|
||||
CBRANCH RCVBLOCKS_XT ; exit on timeout
|
||||
|
||||
; check for EOT -> end
|
||||
LOADC EOT
|
||||
CMP.S0 EQ
|
||||
CBRANCH RCVBLOCKS_XT
|
||||
|
||||
; check for STX -> read block
|
||||
LOADC STX
|
||||
CMP.S0 EQ
|
||||
CBRANCH RCVBLOCKS_CONT
|
||||
|
||||
; anything else -> send NAK
|
||||
BRANCH RCVBLOCKS_RETRY
|
||||
|
||||
RCVBLOCKS_CONT:
|
||||
DROP ; remove header byte
|
||||
DUP ; duplicate pointer
|
||||
LOADC READBLOCK
|
||||
CALL
|
||||
LOADC -1
|
||||
CMP.S0 EQ ; check for timeout
|
||||
CBRANCH RCVBLOCKS_XT ; exit on timeout
|
||||
|
||||
LOADC -2
|
||||
CMP.S0 EQ ; check for checksum error
|
||||
CBRANCH RCVBLOCKS_RETRY
|
||||
|
||||
DROP ; remove return code
|
||||
LOADC L_BLOCKSIZE ; advance pointer
|
||||
ADD
|
||||
|
||||
LOADC ACK ; send ACK
|
||||
LOADC CONOUT
|
||||
CALL
|
||||
|
||||
; next block
|
||||
BRANCH RCVBLOCKS_L
|
||||
|
||||
RCVBLOCKS_RETRY:
|
||||
DROP ; remove read byte
|
||||
; send NAK
|
||||
LOADC NAK
|
||||
LOADC CONOUT
|
||||
CALL
|
||||
|
||||
; next block
|
||||
BRANCH RCVBLOCKS_L
|
||||
|
||||
RCVBLOCKS_XT:
|
||||
DROP ; remove pointer
|
||||
DROP ; remove read byte
|
||||
RET
|
||||
|
||||
|
||||
; ---- read a sequence of binary words and store into memory
|
||||
; - arguments: pointer to memory area
|
||||
READBLOCK:
|
||||
FPADJ -12
|
||||
STORE 0 ; buffer pointer
|
||||
LOADCP L_BLOCKSIZE
|
||||
STORE 4 ; remaining bytes
|
||||
LOADC 0
|
||||
STORE 8 ; checksum
|
||||
READBLOCK_L:
|
||||
LOADCP READWORD ; read a word
|
||||
CALL
|
||||
LOADC -1 ; check for timeout
|
||||
CMP EQ
|
||||
CBRANCH.NZ READBLOCK_ERR
|
||||
|
||||
; DUP ; debug
|
||||
; LOADC PRINTHEXW
|
||||
; CALL
|
||||
|
||||
|
||||
DUP ; duplicate read word
|
||||
LOAD 8 ; load checksum
|
||||
ADD ; checksum = ((checksum + data) ^ pattern) << 1
|
||||
LOADCP CKSUM_PATTERN
|
||||
XOR
|
||||
SHL
|
||||
STORE 8 ; store new checkcsum
|
||||
|
||||
LOAD 0 ; load buffer pointer
|
||||
SWAP ; swap value and pointer for STOREI
|
||||
STOREI 4 ; store word and increment pointer
|
||||
STORE 0 ; store pointer
|
||||
|
||||
LOAD 4 ; load remaining bytes
|
||||
DEC L_WORDSIZE ; decrement by word size
|
||||
DUP
|
||||
STORE 4 ; store
|
||||
CBRANCH.NZ READBLOCK_L ; loop if remaining words not zero
|
||||
|
||||
|
||||
LOADCP READWORD ; read checksum
|
||||
CALL
|
||||
LOADC -1 ; check for timeout
|
||||
CMP EQ
|
||||
CBRANCH READBLOCK_ERR
|
||||
|
||||
LOAD 8 ; load checksum
|
||||
CMP EQ
|
||||
CBRANCH READBLOCK_OK
|
||||
LOADC -2 ; return code for checksum error
|
||||
BRANCH READBLOCK_XT
|
||||
READBLOCK_OK:
|
||||
LOADC 0 ; return 0
|
||||
READBLOCK_XT:
|
||||
FPADJ 12
|
||||
RET
|
||||
|
||||
READBLOCK_ERR:
|
||||
DROP ; remove result
|
||||
LOAD 4 ; return number of missing bytes
|
||||
FPADJ 8
|
||||
RET
|
||||
|
||||
; --- read four bytes (msb to lsb) and return as word
|
||||
; returns: word, error code (-1 for error, 0 otherwise)
|
||||
|
||||
READWORD:
|
||||
LOADCP WAITFORBYTE
|
||||
CALL
|
||||
DUP
|
||||
LOADC -1 ; check for error
|
||||
CMP EQ
|
||||
CBRANCH.NZ READWORD_ERR
|
||||
; first byte is now on stack
|
||||
BROT ; rotate byte left
|
||||
|
||||
LOADCP WAITFORBYTE
|
||||
CALL
|
||||
DUP
|
||||
LOADC -1 ; check for error
|
||||
CMP EQ
|
||||
CBRANCH.NZ READWORD_ERR
|
||||
; second byte is now on stack
|
||||
OR ; OR last byte with this byte
|
||||
BROT ; rotate bytes left
|
||||
|
||||
LOADCP WAITFORBYTE
|
||||
CALL
|
||||
DUP
|
||||
LOADC -1 ; check for error
|
||||
CMP EQ
|
||||
CBRANCH.NZ READWORD_ERR
|
||||
; third byte is now on stack
|
||||
OR ; OR last byte with this byte
|
||||
BROT
|
||||
|
||||
LOADCP WAITFORBYTE
|
||||
CALL
|
||||
DUP
|
||||
LOADC -1 ; check for error
|
||||
CMP EQ
|
||||
CBRANCH.NZ READWORD_ERR
|
||||
; fourth byte is now on stack
|
||||
OR ; OR last byte with this byte
|
||||
|
||||
LOADC 0 ; error code (0: no error)
|
||||
RET
|
||||
|
||||
READWORD_ERR:
|
||||
LOADC -1 ; error code
|
||||
RET
|
||||
|
||||
;---- wait a fixed amount of cycles for a character to be
|
||||
; received on the UART.
|
||||
; returns character or -1 on timeout
|
||||
|
||||
.EQU MAX_WAIT 20000000
|
||||
WAITFORBYTE:
|
||||
LOADCP MAX_WAIT ; maximum wait loops
|
||||
WAITFORBYTE_L:
|
||||
LOADC UART_REG ; address of UART register
|
||||
LOADI ; load status
|
||||
LOADC 512 ; check bit 9 (rx_avail)
|
||||
AND
|
||||
CBRANCH WAITFORBYTE_RX ; if bit 9 is one, a character is available
|
||||
DEC 1
|
||||
DUP
|
||||
CBRANCH.NZ WAITFORBYTE_L
|
||||
DROP ; remove wait counter from stack
|
||||
LOADC -1 ; error code
|
||||
RET
|
||||
WAITFORBYTE_RX:
|
||||
DROP ; remove wait counter from stack
|
||||
LOADC UART_REG
|
||||
LOADI ; read register again
|
||||
LOADC 255 ; mask status bits
|
||||
AND
|
||||
LOADC UART_REG ; I/O address
|
||||
LOADC 512 ; set bit 9 (rx_clear)
|
||||
STOREI ; write register
|
||||
DROP ; remove address left by STOREI
|
||||
RET
|
||||
|
||||
.CPOOL
|
||||
|
||||
;---- boot from SD-card
|
||||
|
||||
; declare buffer addresses used by sdcardlib.s
|
||||
.EQU CSD_BUF 63984
|
||||
.EQU CARD_BUF 64000
|
||||
CMD_B:
|
||||
DROP ; remove input char
|
||||
LOADCP NEWLINE
|
||||
CALL
|
||||
|
||||
FPADJ -4
|
||||
; initialize card
|
||||
LOADC INITSDCARD
|
||||
CALL
|
||||
|
||||
; read partition block
|
||||
LOADC 0
|
||||
LOADC CARDREADBLK
|
||||
CALL
|
||||
|
||||
DUP ; non-zero return code means error
|
||||
CBRANCH.Z CMD_B_1
|
||||
CMD_B_ERR:
|
||||
LOADC PRINTHEXW ; print error code
|
||||
CALL
|
||||
LOADC NEWLINE
|
||||
CALL
|
||||
LOADC 0 ; if we return, we need to
|
||||
; put a fake input char back on the
|
||||
; stack because the main loop will
|
||||
; try to remove it
|
||||
FPADJ 4
|
||||
RET
|
||||
|
||||
CMD_B_1:
|
||||
DROP ; remove error code
|
||||
; check boot partition slot (boot flag)
|
||||
LOADCP CARD_BUF,104 ; offset partition flags second part slot
|
||||
LOADI
|
||||
LOADC 2 ; PartFlags [PartBoot]
|
||||
CMP EQ
|
||||
CBRANCH CMD_B_C
|
||||
|
||||
; no boot partition
|
||||
LOADC $B0
|
||||
BRANCH CMD_B_ERR
|
||||
|
||||
CMD_B_C:
|
||||
; get start block
|
||||
LOADCP CARD_BUF,108 ; offset startBlock
|
||||
LOADI
|
||||
; get block count
|
||||
LOADCP CARD_BUF,124 ; offset bootBlocks
|
||||
LOADI
|
||||
|
||||
FPADJ -4 ; allocate space for address var
|
||||
LOADCP MON_ADDR
|
||||
LOADI
|
||||
STORE 0 ; initialize dest addr
|
||||
CMD_B_L:
|
||||
; read block
|
||||
OVER ; duplicate block no
|
||||
LOADC CARDREADBLK
|
||||
CALL
|
||||
|
||||
DUP ; check for error
|
||||
CBRANCH.Z CMD_B_C2 ; continue if zero (no error)
|
||||
|
||||
NIP ; remove start and count, keep error code
|
||||
NIP
|
||||
BRANCH CMD_B_ERR
|
||||
CMD_B_C2: DROP ; remove error code
|
||||
CMD_B_2:
|
||||
; copy to destination
|
||||
LOAD 0 ; dest addr
|
||||
LOADC COPY_BLK
|
||||
CALL
|
||||
|
||||
; decrement count and loop
|
||||
LOAD 0 ; increment dest addr
|
||||
LOADC 512
|
||||
ADD
|
||||
STORE 0
|
||||
|
||||
SWAP ; swap block no/count, blockno is now ToS
|
||||
INC 1
|
||||
SWAP ; count is now ToS
|
||||
DEC 1
|
||||
DUP
|
||||
CBRANCH.NZ CMD_B_L ; if not zero, loop
|
||||
|
||||
; jump to coreloader
|
||||
DROP
|
||||
DROP
|
||||
FPADJ 4
|
||||
LOADCP MON_ADDR
|
||||
LOADI
|
||||
JUMP
|
||||
|
||||
CMD_B_XT:
|
||||
FPADJ 4
|
||||
RET
|
||||
|
||||
; copy a sdcard block to destination address
|
||||
; block size is always 512 byte, source
|
||||
; is always CARD_BUF
|
||||
; parameters: dest addr
|
||||
COPY_BLK:
|
||||
FPADJ -4
|
||||
LOADC 128 ; word count
|
||||
STORE 0
|
||||
|
||||
LOADCP CARD_BUF ; src addr
|
||||
COPY_BLK1:
|
||||
SWAP
|
||||
; [ src addr, dest addr ]
|
||||
OVER ; [ saddr, daddr, saddr ]
|
||||
LOADI ; [ saddr, daddr, sword ]
|
||||
STOREI 4 ; [ saddr, daddr + 4 ]
|
||||
SWAP ; [ daddr + 4, saddr ]
|
||||
INC 4 ; [ daddr + 4, saddr + 4]
|
||||
|
||||
LOAD 0 ; load and decrement counter
|
||||
DEC 1
|
||||
DUP
|
||||
STORE 0 ; store it again
|
||||
CBRANCH.NZ COPY_BLK1 ; if not zero, loop
|
||||
|
||||
DROP ; remove saddr and daddr
|
||||
DROP
|
||||
|
||||
FPADJ 4
|
||||
RET
|
||||
|
||||
.CPOOL
|
||||
|
||||
; wait approx. 1 millisecond
|
||||
;
|
||||
; 83.333 MHz Clock, three instructions a 4 cycles
|
||||
; 83333 / 12 = 6944.4166
|
||||
; works only if executed without wait states (i.e.
|
||||
; from BRAM/SRAM)
|
||||
WAIT1MSEC:
|
||||
LOADCP 6944
|
||||
WAIT1LOOP:
|
||||
DEC 1
|
||||
DUP
|
||||
CBRANCH.NZ WAIT1LOOP
|
||||
DROP
|
||||
RET
|
||||
|
||||
%include "sdcardboot.s"
|
||||
.CPOOL
|
||||
MESSAGE:
|
||||
.BYTE 13,10,"ROM Monitor v3.0.3", 13, 10,
|
||||
"Set A)ddress D)eposit eX)amine L)oad G)o B)oot",13,10,0
|
||||
PROMPT2:
|
||||
.BYTE "]> ",0
|
||||
END:
|
||||
1939
lib/runtime.s
Normal file
1939
lib/runtime.s
Normal file
File diff suppressed because it is too large
Load diff
613
lib/sdcardboot.s
Normal file
613
lib/sdcardboot.s
Normal file
|
|
@ -0,0 +1,613 @@
|
|||
.EQU SPIREG $880
|
||||
|
||||
.EQU SPI_CTRL_WRITE %100000000000000
|
||||
.EQU SPI_RX_FILTER_EN %010000000000000
|
||||
.EQU SPI_TXRX_EN %001000000000000
|
||||
.EQU SPI_CLK_F_EN %000100000000000
|
||||
.EQU SPI_CLK_DIV_WR %000010000000000
|
||||
.EQU SPI_RX_RD %000001000000000
|
||||
.EQU SPI_TX_WR %000000100000000
|
||||
|
||||
.EQU SPI_C_D %100000000000000
|
||||
.EQU SPI_C_CHG %010000000000000
|
||||
.EQU SPDI_C_BUSY %001000000000000
|
||||
.EQU SPI_TX_RDY %000100000000000
|
||||
.EQU SPI_TX_EMPTY %000010000000000
|
||||
.EQU SPI_RX_AVAIL %000001000000000
|
||||
.EQU SPI_RX_OVR %000000100000000
|
||||
|
||||
.EQU SPI_TXRX_EN_MASK ~SPI_TXRX_EN
|
||||
|
||||
_WAIT:
|
||||
LOADC 10
|
||||
_WAITL:
|
||||
LOADC WAIT1MSEC
|
||||
CALL
|
||||
DEC 1
|
||||
DUP
|
||||
CBRANCH.NZ _WAITL
|
||||
DROP
|
||||
RET
|
||||
|
||||
INITSDCARD:
|
||||
LOADC WAIT1MSEC
|
||||
CALL
|
||||
|
||||
LOADC _SPIINIT1
|
||||
CALL
|
||||
|
||||
;LOADC 'I'
|
||||
;LOADCP CONOUT
|
||||
;CALL
|
||||
|
||||
LOADC _WAITSPITXRDY
|
||||
CALL
|
||||
|
||||
;LOADC 'W'
|
||||
;LOADCP CONOUT
|
||||
;CALL
|
||||
|
||||
; send RESET CARD command
|
||||
LOADC $95 ; send cmd0 with arg 0 and checksum $95
|
||||
LOADC $0
|
||||
LOADC $0
|
||||
LOADC SENDCMD_R1
|
||||
CALL
|
||||
|
||||
DROP ; TODO: handle errors
|
||||
;LOADCP PRINTHEXW ; print status returned by the card
|
||||
;CALL
|
||||
;LOADCP NEWLINE
|
||||
;CALL
|
||||
|
||||
;LOADC '9'
|
||||
;LOADCP CONOUT
|
||||
;CALL
|
||||
|
||||
LOADC _WAITSPITXRDY
|
||||
CALL
|
||||
|
||||
LOADC _WAIT
|
||||
CALL
|
||||
|
||||
;LOADC '1'
|
||||
;LOADCP CONOUT
|
||||
;CALL
|
||||
|
||||
LOADC $87
|
||||
LOADC $01AA
|
||||
LOADC $8
|
||||
LOADC SENDCMD_R7
|
||||
CALL
|
||||
|
||||
DROP
|
||||
|
||||
LOADC _WAITSPITXRDY
|
||||
CALL
|
||||
|
||||
;LOADC '2'
|
||||
;LOADCP CONOUT
|
||||
;CALL
|
||||
|
||||
;LOADCP _WAIT
|
||||
;CALL
|
||||
|
||||
;LOADC '.'
|
||||
;LOADCP CONOUT
|
||||
;CALL
|
||||
|
||||
LOADC CARDINITV2
|
||||
CALL
|
||||
|
||||
;LOADC '+'
|
||||
;LOADCP CONOUT
|
||||
;CALL
|
||||
|
||||
|
||||
LOADC CARDFASTCLK
|
||||
CALL
|
||||
|
||||
;LOADC '3'
|
||||
;LOADCP CONOUT
|
||||
;CALL
|
||||
|
||||
; CMD16: set block size to 512 byte
|
||||
LOADC 0
|
||||
LOADC 512
|
||||
LOADC 16
|
||||
LOADCP SENDCMD_R1
|
||||
CALL
|
||||
|
||||
DROP
|
||||
|
||||
;LOADCP _WAIT
|
||||
;CALL
|
||||
|
||||
;LOADC '4'
|
||||
;LOADCP CONOUT
|
||||
;CALL
|
||||
|
||||
RET
|
||||
|
||||
; read a 512-byte-block from the card
|
||||
; args: block number
|
||||
; returns: 0 on success
|
||||
CARDREADBLK:
|
||||
LOADC 128 ; number of words in a block
|
||||
SWAP ; move block number up the stack
|
||||
LOADCP CARD_BUF
|
||||
SWAP
|
||||
LOADC 0
|
||||
SWAP
|
||||
LOADC 17 ; CMD17: read block
|
||||
LOADC SENDCMD_PKT
|
||||
CALL
|
||||
RET
|
||||
|
||||
; send the card initialization command
|
||||
; wait until the card responds
|
||||
CARDINITV2:
|
||||
LOADC 100 ; try up to 100 times
|
||||
CARD_LOOP1:
|
||||
LOADC 50 ; wait 50 msec
|
||||
CARD_LOOP2:
|
||||
LOADC WAIT1MSEC
|
||||
CALL
|
||||
DEC 1
|
||||
DUP
|
||||
CBRANCH.NZ CARD_LOOP2
|
||||
DROP ; remove loop count value
|
||||
|
||||
LOADC $0
|
||||
LOADC $0
|
||||
LOADC 58
|
||||
LOADC SENDCMD_R7 ; send CMD58
|
||||
CALL
|
||||
DROP ; ignore result (why?)
|
||||
|
||||
LOADC $0
|
||||
LOADCP $40000000
|
||||
LOADC 41
|
||||
LOADC SENDACMD_R1 ; send ACMD41
|
||||
CALL
|
||||
|
||||
CBRANCH.Z CARD_OK ; if result is zero, the command succeded
|
||||
; and the card initialization is finished
|
||||
DEC 1
|
||||
DUP
|
||||
CBRANCH.NZ CARD_LOOP1
|
||||
DROP ; remove outer loop count value
|
||||
|
||||
RET
|
||||
CARD_OK:
|
||||
DROP ; remove outer loop count value
|
||||
|
||||
; CMD16: set block size to 512 byte
|
||||
LOADC 0
|
||||
LOADC 512
|
||||
LOADC 16
|
||||
LOADC SENDCMD_R1
|
||||
CALL
|
||||
DROP ; ignore return value
|
||||
|
||||
RET
|
||||
|
||||
; set fast transfer rate
|
||||
CARDFASTCLK:
|
||||
LOADC SPIREG
|
||||
; set clock divider to ~2,6MHz
|
||||
LOADC SPI_CLK_DIV_WR+10
|
||||
STOREI
|
||||
DROP
|
||||
RET
|
||||
|
||||
; perform first phase of card initialization
|
||||
; which is to enable clock and wait a bit
|
||||
; leaves the clock running
|
||||
_SPIINIT1:
|
||||
LOADC SPIREG
|
||||
; set clock divider to ~325KHz
|
||||
LOADC SPI_CLK_DIV_WR+64
|
||||
STOREI
|
||||
DROP
|
||||
|
||||
; clear all flags + enable clock
|
||||
; /CS and MOSI are default high
|
||||
LOADC SPIREG
|
||||
LOADCP SPI_CTRL_WRITE,SPI_CLK_F_EN
|
||||
STOREI
|
||||
DROP
|
||||
|
||||
; we should wait at least for 74 clock cycles now
|
||||
LOADC 2 ; wait 2 msec, that should be ~300 cycles
|
||||
_SPIINIT1L:
|
||||
LOADC WAIT1MSEC
|
||||
CALL
|
||||
DEC 1
|
||||
DUP
|
||||
CBRANCH.NZ _SPIINIT1L
|
||||
DROP
|
||||
|
||||
LOADC SPIREG
|
||||
LOADCP SPI_CTRL_WRITE ; disable clock
|
||||
STOREI
|
||||
DROP
|
||||
|
||||
LOADCP WAIT1MSEC
|
||||
CALL
|
||||
|
||||
RET
|
||||
|
||||
; wait for transmission to finish
|
||||
; (wait for TX_EMPTY bit)
|
||||
_SPIWAITTX:
|
||||
LOADC SPIREG
|
||||
LOADI
|
||||
LOADCP SPI_TX_EMPTY
|
||||
AND
|
||||
CBRANCH.Z _SPIWAITTX
|
||||
RET
|
||||
|
||||
; finalize a command that has been sent:
|
||||
; wait until the transmitter is idle
|
||||
; then disable clock and set MOSI high
|
||||
_SPIENDCMD:
|
||||
LOADC $FF
|
||||
LOADC _SENDBYTE
|
||||
CALL
|
||||
LOADC $FF
|
||||
LOADC _SENDBYTE
|
||||
CALL
|
||||
|
||||
;LOADC 'E'
|
||||
;LOADCP CONOUT
|
||||
;CALL
|
||||
|
||||
LOADC _SPIWAITTX
|
||||
CALL
|
||||
|
||||
;LOADC 'w'
|
||||
;LOADCP CONOUT
|
||||
;CALL
|
||||
LOADC _WAIT_S ; wait a short time
|
||||
CALL
|
||||
|
||||
LOADC SPIREG
|
||||
LOADCP SPI_IDLE_FLAGS ; turn off transceiver
|
||||
LOADI
|
||||
STOREI
|
||||
DROP
|
||||
|
||||
; wait for a few instructions
|
||||
LOADC 100
|
||||
SPIEND_LP: DEC 1
|
||||
DUP
|
||||
CBRANCH.NZ SPIEND_LP
|
||||
DROP
|
||||
|
||||
RET
|
||||
|
||||
_WAIT_S:
|
||||
LOADC 100
|
||||
_WAIT_S_L:
|
||||
DEC 1
|
||||
DUP
|
||||
CBRANCH.NZ _WAIT_S_L
|
||||
DROP
|
||||
RET
|
||||
|
||||
; clear RX fifo
|
||||
CLEAR_RX_FIFO:
|
||||
CLEAR_RX_L1:
|
||||
LOADC SPIREG
|
||||
LOADI
|
||||
|
||||
;DUP
|
||||
;LOADCP PRINTHEXW
|
||||
;CALL
|
||||
;LOADCP NEWLINE
|
||||
;CALL
|
||||
|
||||
LOADC SPI_RX_AVAIL
|
||||
AND
|
||||
CBRANCH.Z CLEAR_RX_X
|
||||
LOADC SPIREG
|
||||
LOADC SPI_RX_RD
|
||||
STOREI
|
||||
DROP
|
||||
|
||||
; FIXME: it seems that this
|
||||
; does not remove a byte from the fifo,
|
||||
; rx_avail stays on, but only after the first
|
||||
; byte has been received and read
|
||||
|
||||
;LOADC 'x'
|
||||
;LOADCP CONOUT
|
||||
;CALL
|
||||
|
||||
BRANCH CLEAR_RX_L1
|
||||
CLEAR_RX_X:
|
||||
RET
|
||||
|
||||
_WAITSPITXRDY:
|
||||
LOADC SPIREG
|
||||
LOADI
|
||||
LOADCP SPI_TX_RDY
|
||||
AND
|
||||
CBRANCH.Z _WAITSPITXRDY
|
||||
RET
|
||||
|
||||
; send a command and receive a data packet response
|
||||
; args: packet size in words, buffer pointer
|
||||
; checksum byte, 32-bit cmd arg, cmd number
|
||||
; returns: 0 on success
|
||||
SENDCMD_PKT:
|
||||
; first send the command
|
||||
LOADC SENDCMD_0
|
||||
CALL
|
||||
|
||||
LOADC _RCVBYTE ; receive R1 response
|
||||
CALL
|
||||
|
||||
CBRANCH.NZ SENDCMD_PKT_E ; on success we get 0
|
||||
|
||||
; now wait for data token
|
||||
SENDCMD_PKT_L:
|
||||
LOADC _RCVBYTE
|
||||
CALL
|
||||
LOADC $FF
|
||||
CMP EQ
|
||||
CBRANCH SENDCMD_PKT_L
|
||||
|
||||
; parameters for _RCVWORDS are on the stack now
|
||||
LOADC _RCVWORDS
|
||||
CALL
|
||||
|
||||
; receive 2 crc bytes
|
||||
LOADC _RCVBYTE
|
||||
CALL
|
||||
BROT
|
||||
LOADC _RCVBYTE
|
||||
CALL
|
||||
OR
|
||||
|
||||
; terminate command
|
||||
LOADC _SPIENDCMD
|
||||
CALL
|
||||
|
||||
DROP ; we ignore the checksum for now
|
||||
|
||||
LOADC 0
|
||||
RET
|
||||
SENDCMD_PKT_E:
|
||||
DROP ; remove remaining args
|
||||
DROP
|
||||
LOADC -1 ; return code for error
|
||||
RET
|
||||
|
||||
; send a command and receive a 1-byte-response (R1)
|
||||
; args: checksum byte, 32-bit cmd arg, cmd number
|
||||
; returns: received byte
|
||||
SENDCMD_R1:
|
||||
LOADC SENDCMD_0
|
||||
CALL
|
||||
|
||||
LOADC _RCVBYTE
|
||||
CALL
|
||||
|
||||
;LOADC 'R'
|
||||
;LOADCP CONOUT
|
||||
;CALL
|
||||
|
||||
;terminate command (/cs high, disable clock)
|
||||
LOADC _SPIENDCMD
|
||||
CALL
|
||||
|
||||
RET
|
||||
|
||||
; send a command
|
||||
; args: checksum byte, 32-bit cmd arg, cmd number
|
||||
SENDCMD_0:
|
||||
; clear RX FIFO first
|
||||
LOADC CLEAR_RX_FIFO
|
||||
CALL
|
||||
|
||||
;LOADC '>'
|
||||
;LOADCP CONOUT
|
||||
;CALL
|
||||
|
||||
; cmd byte is at TOS at this point
|
||||
LOADC $40 ; or in start of frame bit
|
||||
OR
|
||||
LOADC _SENDBYTE
|
||||
CALL
|
||||
; cmd arg is at TOS now
|
||||
LOADC _SENDWORD
|
||||
CALL
|
||||
; checksum byte is at TOS now
|
||||
LOADC _SENDBYTE
|
||||
CALL
|
||||
|
||||
LOADC _XCVR_ENABLE ; enable transceiver last,
|
||||
CALL ; a complete command should
|
||||
RET ; fit into the tx fifo
|
||||
|
||||
; send ACMD and receive a 1-byte-response (R1)
|
||||
; args: checksum byte, 32-bit cmd arg, ACMD number
|
||||
; returns: received byte or -1 if first response byte
|
||||
; indicated an error
|
||||
SENDACMD_R1:
|
||||
LOADC $0
|
||||
LOADC $0
|
||||
LOADC 55 ; send CMD55
|
||||
LOADC SENDCMD_R1
|
||||
CALL
|
||||
|
||||
LOADC 1 ; 1 = idle state, no errors
|
||||
CMP NE
|
||||
CBRANCH.NZ SENDACMD_ERR
|
||||
|
||||
; pass our args to SENDCMD_R1
|
||||
LOADC SENDCMD_R1
|
||||
CALL
|
||||
RET
|
||||
|
||||
SENDACMD_ERR:
|
||||
LOADCP -1
|
||||
RET
|
||||
|
||||
; send a command and receive a 4+1-byte-response (R7)
|
||||
; args: checksum byte, 32-bit cmd arg, cmd number
|
||||
; returns: received word or -1 if first response byte
|
||||
; indicated an error
|
||||
|
||||
SENDCMD_R7:
|
||||
; send the command
|
||||
LOADC SENDCMD_0
|
||||
CALL
|
||||
|
||||
;LOADC '7'
|
||||
;LOADCP CONOUT
|
||||
;CALL
|
||||
|
||||
LOADC _RCVBYTE
|
||||
CALL
|
||||
|
||||
LOADC _RCVWORD
|
||||
CALL
|
||||
|
||||
;terminate command (/cs high, disable clock)
|
||||
LOADC _SPIENDCMD
|
||||
CALL
|
||||
|
||||
SWAP ; swap 1st response byte with received word
|
||||
LOADC %011111110 ; check for any error flags
|
||||
AND
|
||||
CBRANCH.Z SENDCMD_R7_NOERR
|
||||
DROP
|
||||
LOADC -1
|
||||
SENDCMD_R7_NOERR:
|
||||
RET
|
||||
|
||||
; send a word as 4 bytes, msb first
|
||||
_SENDWORD:
|
||||
DUP ; remember original value for later
|
||||
|
||||
BROT ; rotate msb to lsb (byte 0)
|
||||
LOADC 255
|
||||
AND.S0 ; isolate byte, keep previous value
|
||||
LOADC _SENDBYTE
|
||||
CALL
|
||||
|
||||
BROT ; byte 1
|
||||
LOADC 255
|
||||
AND.S0
|
||||
LOADC _SENDBYTE
|
||||
CALL
|
||||
|
||||
BROT ; byte 2
|
||||
LOADC 255
|
||||
AND
|
||||
LOADC _SENDBYTE
|
||||
CALL
|
||||
|
||||
; byte 3 is already on the stack
|
||||
LOADC 255
|
||||
AND
|
||||
LOADC _SENDBYTE
|
||||
CALL
|
||||
|
||||
RET
|
||||
|
||||
; receive multiple 4-byte-words and store into
|
||||
; memory buffer
|
||||
; args: number of words, pointer to buffer
|
||||
_RCVWORDS:
|
||||
FPADJ -4
|
||||
STORE 0 ; store pointer arg into local variable
|
||||
; keep counter on stack
|
||||
_RCVWORDS_LP:
|
||||
LOAD 0 ; load buf pointer for STOREI
|
||||
LOADC _RCVWORD
|
||||
CALL ; receive a word
|
||||
STOREI 4 ; store to buf with postincrement
|
||||
STORE 0 ; store pointer variable
|
||||
|
||||
DEC 1 ; decrement word counter
|
||||
DUP
|
||||
CBRANCH.NZ _RCVWORDS_LP ; if not null, loop
|
||||
DROP ; remove counter value
|
||||
|
||||
FPADJ 4
|
||||
RET
|
||||
|
||||
; receive 4 bytes, return as word
|
||||
_RCVWORD:
|
||||
LOADC _RCVBYTE ; receive first byte
|
||||
CALL
|
||||
BROT ; rotate byte to left
|
||||
|
||||
LOADC _RCVBYTE ; receive second byte
|
||||
CALL
|
||||
OR ; or first and second byte together
|
||||
BROT ; rotate 1st + 2nd to left
|
||||
|
||||
LOADC _RCVBYTE ; receive third byte
|
||||
CALL
|
||||
OR
|
||||
BROT
|
||||
|
||||
LOADCP _RCVBYTE ; receive fourth byte
|
||||
CALL
|
||||
OR
|
||||
RET
|
||||
|
||||
_XCVR_ENABLE:
|
||||
LOADC SPIREG
|
||||
LOADC SPI_TX_FLAGS
|
||||
LOADI
|
||||
STOREI
|
||||
DROP
|
||||
RET
|
||||
|
||||
; send a byte
|
||||
; args: byte to be sent
|
||||
_SENDBYTE:
|
||||
LOADC SPIREG
|
||||
LOADI ; load spi io register
|
||||
LOADCP SPI_TX_RDY
|
||||
AND ; check tx_rdy bit
|
||||
CBRANCH.Z _SENDBYTE ; if not set, loop
|
||||
|
||||
LOADC SPI_TX_WR ; TX_WR bit
|
||||
OR ; OR in byte to be send
|
||||
|
||||
LOADC SPIREG
|
||||
SWAP ; swap value and addr for STOREI
|
||||
STOREI ; store word (flags + data) to io register
|
||||
DROP ; remove STOREI result
|
||||
|
||||
RET
|
||||
|
||||
; receive a byte. receiver must be enabled.
|
||||
; returns: received byte
|
||||
_RCVBYTE:
|
||||
LOADC SPIREG
|
||||
LOADI ; load spi io register
|
||||
LOADC SPI_RX_AVAIL
|
||||
AND.S0 ; check rx_avail bit, keep original value
|
||||
CBRANCH.NZ RECVGOTIT
|
||||
DROP ; rx_avail not set, remove register value and loop
|
||||
BRANCH _RCVBYTE
|
||||
RECVGOTIT:
|
||||
LOADC SPIREG
|
||||
LOADC SPI_RX_RD ; remove one byte from rx fifo
|
||||
STOREI
|
||||
DROP
|
||||
|
||||
LOADC 255
|
||||
AND ; keep bits 7-0
|
||||
RET
|
||||
|
||||
SPI_TX_FLAGS: .WORD SPI_CTRL_WRITE + SPI_TXRX_EN + SPI_RX_FILTER_EN
|
||||
SPI_IDLE_FLAGS: .WORD SPI_CTRL_WRITE
|
||||
795
lib/sdcardlib.s
Normal file
795
lib/sdcardlib.s
Normal file
|
|
@ -0,0 +1,795 @@
|
|||
; Copyright 2021-2024 Sebastian Lederer. See the file LICENSE.md for details
|
||||
.EQU SPIREG $880
|
||||
|
||||
.EQU SPI_CTRL_WRITE %100000000000000
|
||||
.EQU SPI_RX_FILTER_EN %010000000000000
|
||||
.EQU SPI_TXRX_EN %001000000000000
|
||||
.EQU SPI_CLK_F_EN %000100000000000
|
||||
.EQU SPI_CLK_DIV_WR %000010000000000
|
||||
.EQU SPI_RX_RD %000001000000000
|
||||
.EQU SPI_TX_WR %000000100000000
|
||||
|
||||
.EQU SPI_C_D %100000000000000
|
||||
.EQU SPI_C_CHG %010000000000000
|
||||
.EQU SPDI_C_BUSY %001000000000000
|
||||
.EQU SPI_TX_RDY %000100000000000
|
||||
.EQU SPI_TX_EMPTY %000010000000000
|
||||
.EQU SPI_RX_AVAIL %000001000000000
|
||||
.EQU SPI_RX_OVR %000000100000000
|
||||
|
||||
.EQU SPI_TXRX_EN_MASK ~SPI_TXRX_EN
|
||||
|
||||
_WAIT:
|
||||
LOADC 10
|
||||
_WAITL:
|
||||
LOADCP WAIT1MSEC
|
||||
CALL
|
||||
DEC 1
|
||||
DUP
|
||||
CBRANCH.NZ _WAITL
|
||||
DROP
|
||||
RET
|
||||
|
||||
INITSDCARD:
|
||||
LOADCP WAIT1MSEC
|
||||
CALL
|
||||
|
||||
LOADCP _SPIINIT1
|
||||
CALL
|
||||
|
||||
;LOADC 'I'
|
||||
;LOADCP CONOUT
|
||||
;CALL
|
||||
|
||||
LOADCP _WAITSPITXRDY
|
||||
CALL
|
||||
|
||||
;LOADC 'W'
|
||||
;LOADCP CONOUT
|
||||
;CALL
|
||||
|
||||
; send RESET CARD command
|
||||
LOADC $95 ; send cmd0 with arg 0 and checksum $95
|
||||
LOADC $0
|
||||
LOADC $0
|
||||
LOADCP SENDCMD_R1
|
||||
CALL
|
||||
|
||||
DROP ; TODO: handle errors
|
||||
;LOADCP PRINTHEXW ; print status returned by the card
|
||||
;CALL
|
||||
;LOADCP NEWLINE
|
||||
;CALL
|
||||
|
||||
;LOADC '9'
|
||||
;LOADCP CONOUT
|
||||
;CALL
|
||||
|
||||
LOADCP _WAITSPITXRDY
|
||||
CALL
|
||||
|
||||
LOADCP _WAIT
|
||||
CALL
|
||||
|
||||
;LOADC '1'
|
||||
;LOADCP CONOUT
|
||||
;CALL
|
||||
|
||||
LOADC $87
|
||||
LOADC $01AA
|
||||
LOADC $8
|
||||
LOADCP SENDCMD_R7
|
||||
CALL
|
||||
|
||||
DROP
|
||||
|
||||
LOADCP _WAITSPITXRDY
|
||||
CALL
|
||||
|
||||
;LOADC '2'
|
||||
;LOADCP CONOUT
|
||||
;CALL
|
||||
|
||||
;LOADCP _WAIT
|
||||
;CALL
|
||||
|
||||
;LOADC '.'
|
||||
;LOADCP CONOUT
|
||||
;CALL
|
||||
|
||||
LOADCP CARDINITV2
|
||||
CALL
|
||||
|
||||
;LOADC '+'
|
||||
;LOADCP CONOUT
|
||||
;CALL
|
||||
|
||||
|
||||
LOADCP CARDFASTCLK
|
||||
CALL
|
||||
|
||||
;LOADC '3'
|
||||
;LOADCP CONOUT
|
||||
;CALL
|
||||
|
||||
; CMD16: set block size to 512 byte
|
||||
LOADC 0
|
||||
LOADC 512
|
||||
LOADC 16
|
||||
LOADCP SENDCMD_R1
|
||||
CALL
|
||||
|
||||
DROP
|
||||
|
||||
;LOADCP _WAIT
|
||||
;CALL
|
||||
|
||||
;LOADC '4'
|
||||
;LOADCP CONOUT
|
||||
;CALL
|
||||
|
||||
RET
|
||||
|
||||
; read a 512-byte-block from the card
|
||||
; args: block number
|
||||
; returns: 0 on success
|
||||
CARDREADBLK:
|
||||
LOADC 128 ; number of words in a block
|
||||
SWAP ; move block number up the stack
|
||||
LOADCP CARD_BUF
|
||||
SWAP
|
||||
LOADC 0
|
||||
SWAP
|
||||
LOADC 17 ; CMD17: read block
|
||||
LOADCP SENDCMD_PKT
|
||||
CALL
|
||||
RET
|
||||
|
||||
; determine number of blocks
|
||||
; returns: number of blocks or -1 on error
|
||||
CARDSIZE:
|
||||
LOADC 4
|
||||
LOADCP CSD_BUF
|
||||
LOADC 0
|
||||
LOADC 0
|
||||
LOADC 9
|
||||
LOADCP SENDCMD_PKT ; send CMD9
|
||||
CALL
|
||||
|
||||
CBRANCH.NZ CARDSIZE_ERR ; if response is zero, an error occurred
|
||||
|
||||
; take bytes 7, 8 and 9 from CSD data
|
||||
; and add 1 to get card size in ksectors
|
||||
LOADCP CSD_BUF
|
||||
INC 4
|
||||
LOADI
|
||||
LOADC $3F ; get byte 7 (bits 22-16)
|
||||
AND
|
||||
BROT
|
||||
BROT
|
||||
|
||||
LOADCP CSD_BUF ; get bytes 8 and 9 (bits 15-0)
|
||||
INC 8
|
||||
LOADI
|
||||
BROT
|
||||
BROT
|
||||
LOADCP $FFFF
|
||||
AND
|
||||
|
||||
OR
|
||||
|
||||
INC 1
|
||||
|
||||
BROT
|
||||
SHL 2; multiply by 1024 to get size in sectors
|
||||
|
||||
RET
|
||||
|
||||
CARDSIZE_ERR:
|
||||
LOADC -1
|
||||
RET
|
||||
|
||||
; returns 1 if the card was changed, 0 otherwise
|
||||
CARDCHANGED:
|
||||
LOADCP SPIREG
|
||||
LOADI
|
||||
LOADCP SPI_C_CHG
|
||||
AND
|
||||
LOADC 0
|
||||
CMPU NE
|
||||
RET
|
||||
|
||||
; write a 512-byte-block to the card
|
||||
; args: block number
|
||||
; returns: 0 on success
|
||||
CARDWRITEBLK:
|
||||
LOADC 128 ; number of words in a block
|
||||
SWAP ; move block number up the stack
|
||||
LOADCP CARD_BUF
|
||||
SWAP
|
||||
LOADC 0
|
||||
SWAP
|
||||
LOADC 24 ; CMD24: write block
|
||||
LOADCP SENDCMD_TXPKT
|
||||
CALL
|
||||
RET
|
||||
; send the card initialization command
|
||||
; wait until the card responds
|
||||
CARDINITV2:
|
||||
LOADC 100 ; try up to 100 times
|
||||
CARD_LOOP1:
|
||||
LOADC 50 ; wait 50 msec
|
||||
CARD_LOOP2:
|
||||
LOADCP WAIT1MSEC
|
||||
CALL
|
||||
DEC 1
|
||||
DUP
|
||||
CBRANCH.NZ CARD_LOOP2
|
||||
DROP ; remove loop count value
|
||||
|
||||
LOADC $0
|
||||
LOADC $0
|
||||
LOADC 58
|
||||
LOADCP SENDCMD_R7 ; send CMD58
|
||||
CALL
|
||||
DROP ; ignore result (why?)
|
||||
|
||||
LOADC $0
|
||||
LOADCP $40000000
|
||||
LOADC 41
|
||||
LOADCP SENDACMD_R1 ; send ACMD41
|
||||
CALL
|
||||
|
||||
CBRANCH.Z CARD_OK ; if result is zero, the command succeded
|
||||
; and the card initialization is finished
|
||||
DEC 1
|
||||
DUP
|
||||
CBRANCH.NZ CARD_LOOP1
|
||||
DROP ; remove outer loop count value
|
||||
|
||||
RET
|
||||
CARD_OK:
|
||||
DROP ; remove outer loop count value
|
||||
|
||||
; CMD16: set block size to 512 byte
|
||||
LOADC 0
|
||||
LOADC 512
|
||||
LOADC 16
|
||||
LOADCP SENDCMD_R1
|
||||
CALL
|
||||
DROP ; ignore return value
|
||||
|
||||
RET
|
||||
|
||||
; set fast transfer rate
|
||||
CARDFASTCLK:
|
||||
LOADC SPIREG
|
||||
; set clock divider to ~2,6MHz
|
||||
LOADCP SPI_CLK_DIV_WR,10 ; using the LOADCP with offset syntax here
|
||||
STOREI
|
||||
DROP
|
||||
RET
|
||||
|
||||
; perform first phase of card initialization
|
||||
; which is to enable clock and wait a bit
|
||||
; leaves the clock running
|
||||
_SPIINIT1:
|
||||
LOADC SPIREG
|
||||
; set clock divider to ~325KHz
|
||||
LOADCP SPI_CLK_DIV_WR,64 ; LOADCP with offset
|
||||
STOREI
|
||||
DROP
|
||||
|
||||
; clear all flags + enable clock
|
||||
; /CS and MOSI are default high
|
||||
LOADC SPIREG
|
||||
LOADCP SPI_CTRL_WRITE,SPI_CLK_F_EN
|
||||
STOREI
|
||||
DROP
|
||||
|
||||
; we should wait at least for 74 clock cycles now
|
||||
LOADC 2 ; wait 2 msec, that should be ~300 cycles
|
||||
_SPIINIT1L:
|
||||
LOADCP WAIT1MSEC
|
||||
CALL
|
||||
DEC 1
|
||||
DUP
|
||||
CBRANCH.NZ _SPIINIT1L
|
||||
DROP
|
||||
|
||||
LOADC SPIREG
|
||||
LOADCP SPI_CTRL_WRITE ; disable clock
|
||||
STOREI
|
||||
DROP
|
||||
|
||||
LOADCP WAIT1MSEC
|
||||
CALL
|
||||
|
||||
RET
|
||||
|
||||
; wait for transmission to finish
|
||||
; (wait for TX_EMPTY bit)
|
||||
_SPIWAITTX:
|
||||
LOADC SPIREG
|
||||
LOADI
|
||||
LOADCP SPI_TX_EMPTY
|
||||
AND
|
||||
CBRANCH.Z _SPIWAITTX
|
||||
RET
|
||||
|
||||
; finalize a command that has been sent:
|
||||
; wait until the transmitter is idle
|
||||
; then disable clock and set MOSI high
|
||||
_SPIENDCMD:
|
||||
LOADCP $FF
|
||||
LOADCP _SENDBYTE
|
||||
CALL
|
||||
LOADCP $FF
|
||||
LOADCP _SENDBYTE
|
||||
CALL
|
||||
|
||||
;LOADC 'E'
|
||||
;LOADCP CONOUT
|
||||
;CALL
|
||||
|
||||
LOADCP _SPIWAITTX
|
||||
CALL
|
||||
|
||||
;LOADC 'w'
|
||||
;LOADCP CONOUT
|
||||
;CALL
|
||||
LOADCP _WAIT_S ; wait a short time
|
||||
CALL
|
||||
|
||||
LOADC SPIREG
|
||||
LOADCP SPI_IDLE_FLAGS ; turn off transceiver
|
||||
LOADI
|
||||
STOREI
|
||||
DROP
|
||||
|
||||
; wait for a few instructions
|
||||
LOADC 100
|
||||
SPIEND_LP: DEC 1
|
||||
DUP
|
||||
CBRANCH.NZ SPIEND_LP
|
||||
DROP
|
||||
|
||||
RET
|
||||
|
||||
_WAIT_S:
|
||||
LOADC 100
|
||||
_WAIT_S_L:
|
||||
DEC 1
|
||||
DUP
|
||||
CBRANCH.NZ _WAIT_S_L
|
||||
DROP
|
||||
RET
|
||||
|
||||
; clear RX fifo
|
||||
CLEAR_RX_FIFO:
|
||||
CLEAR_RX_L1:
|
||||
LOADC SPIREG
|
||||
LOADI
|
||||
|
||||
;DUP
|
||||
;LOADCP PRINTHEXW
|
||||
;CALL
|
||||
;LOADCP NEWLINE
|
||||
;CALL
|
||||
|
||||
LOADC SPI_RX_AVAIL
|
||||
AND
|
||||
CBRANCH.Z CLEAR_RX_X
|
||||
LOADC SPIREG
|
||||
LOADC SPI_RX_RD
|
||||
STOREI
|
||||
DROP
|
||||
|
||||
; FIXME: it seems that this
|
||||
; does not remove a byte from the fifo,
|
||||
; rx_avail stays on, but only after the first
|
||||
; byte has been received and read
|
||||
|
||||
;LOADC 'x'
|
||||
;LOADCP CONOUT
|
||||
;CALL
|
||||
|
||||
BRANCH CLEAR_RX_L1
|
||||
CLEAR_RX_X:
|
||||
RET
|
||||
|
||||
_WAITSPITXRDY:
|
||||
LOADC SPIREG
|
||||
LOADI
|
||||
LOADCP SPI_TX_RDY
|
||||
AND
|
||||
CBRANCH.Z _WAITSPITXRDY
|
||||
RET
|
||||
|
||||
; send a command and receive a data packet response
|
||||
; args: packet size in words, buffer pointer
|
||||
; checksum byte, 32-bit cmd arg, cmd number
|
||||
; returns: 0 on success
|
||||
SENDCMD_PKT:
|
||||
; first send the command
|
||||
LOADCP SENDCMD_0
|
||||
CALL
|
||||
|
||||
LOADCP _RCVBYTE ; receive R1 response
|
||||
CALL
|
||||
|
||||
CBRANCH.NZ SENDCMD_PKT_E ; on success we get 0
|
||||
|
||||
; now wait for data token
|
||||
SENDCMD_PKT_L:
|
||||
LOADCP _RCVBYTE
|
||||
CALL
|
||||
LOADC $FF
|
||||
CMP EQ
|
||||
CBRANCH SENDCMD_PKT_L
|
||||
|
||||
; parameters for _RCVWORDS are on the stack now
|
||||
LOADCP _RCVWORDS
|
||||
CALL
|
||||
|
||||
; receive 2 crc bytes
|
||||
LOADCP _RCVBYTE
|
||||
CALL
|
||||
BROT
|
||||
LOADCP _RCVBYTE
|
||||
CALL
|
||||
OR
|
||||
|
||||
; terminate command
|
||||
LOADCP _SPIENDCMD
|
||||
CALL
|
||||
|
||||
DROP ; we ignore the checksum for now
|
||||
|
||||
LOADC 0
|
||||
RET
|
||||
SENDCMD_PKT_E:
|
||||
DROP ; remove remaining args
|
||||
DROP
|
||||
LOADC -1 ; return code for error
|
||||
RET
|
||||
|
||||
; send a command and send a data packet
|
||||
; args: packet size in words, buffer pointer
|
||||
; checksum byte, 32-bit cmd arg, cmd number
|
||||
; returns: 0 on success
|
||||
SENDCMD_TXPKT:
|
||||
; first send the command
|
||||
LOADCP SENDCMD_0
|
||||
CALL
|
||||
|
||||
;LOADCP _RCVBYTE
|
||||
;CALL
|
||||
;DROP ; remove byte received during transmit
|
||||
|
||||
LOADCP _RCVBYTE ; receive R1 response
|
||||
CALL
|
||||
|
||||
CBRANCH.NZ SENDCMD_TXPKT_E ; on error we get nonzero
|
||||
|
||||
; send stuff byte
|
||||
LOADC $FF
|
||||
LOADCP _SENDBYTE
|
||||
CALL
|
||||
|
||||
; now send data token
|
||||
LOADCP %11111110
|
||||
LOADCP _SENDBYTE
|
||||
CALL
|
||||
|
||||
; send data block
|
||||
; parameters for _SENDWORDS are on the stack now
|
||||
LOADCP _SENDWORDS
|
||||
CALL
|
||||
|
||||
; send 2 dummy crc bytes
|
||||
LOADC 0
|
||||
LOADCP _SENDBYTE
|
||||
CALL
|
||||
LOADC 0
|
||||
LOADCP _SENDBYTE
|
||||
CALL
|
||||
|
||||
;receive data response byte
|
||||
SENDCMD_TXPKT_LR:
|
||||
LOADCP _RCVBYTE
|
||||
CALL
|
||||
LOADC $FF ; discard $FF bytes
|
||||
CMP.S0 NE
|
||||
CBRANCH SENDCMD_TXPKT_CT
|
||||
DROP
|
||||
BRANCH SENDCMD_TXPKT_LR
|
||||
|
||||
SENDCMD_TXPKT_CT:
|
||||
LOADC $1F
|
||||
AND ; isolate status bits
|
||||
LOADC $5 ; command accepted bit set?
|
||||
CMP NE ; if not, exit with error
|
||||
CBRANCH SENDCMD_TXPKT_E2
|
||||
|
||||
; wait until card is busy
|
||||
SENDCMD_TXPKT_LB:
|
||||
LOADCP _RCVBYTE ; receive byte
|
||||
CALL
|
||||
CBRANCH.NZ SENDCMD_TXPKT_LB ; loop until byte is 0
|
||||
; wait until card is not busy
|
||||
SENDCMD_TXPKT_L2:
|
||||
LOADCP _RCVBYTE ;receive byte
|
||||
CALL
|
||||
CBRANCH.Z SENDCMD_TXPKT_L2 ; loop if byte is 0 (i.e. MISO is held low)
|
||||
|
||||
LOADC 0
|
||||
BRANCH SENDCMD_TXPKT_X
|
||||
|
||||
SENDCMD_TXPKT_E:
|
||||
DROP ; remove remaining args
|
||||
DROP
|
||||
SENDCMD_TXPKT_E2:
|
||||
LOADC -1 ; return code for error
|
||||
SENDCMD_TXPKT_X:
|
||||
; terminate command
|
||||
LOADCP _SPIENDCMD
|
||||
CALL
|
||||
|
||||
RET
|
||||
|
||||
; send a command and receive a 1-byte-response (R1)
|
||||
; args: checksum byte, 32-bit cmd arg, cmd number
|
||||
; returns: received byte
|
||||
SENDCMD_R1:
|
||||
LOADCP SENDCMD_0
|
||||
CALL
|
||||
|
||||
LOADCP _RCVBYTE
|
||||
CALL
|
||||
|
||||
;LOADC 'R'
|
||||
;LOADCP CONOUT
|
||||
;CALL
|
||||
|
||||
;terminate command (/cs high, disable clock)
|
||||
LOADCP _SPIENDCMD
|
||||
CALL
|
||||
|
||||
RET
|
||||
|
||||
; send a command
|
||||
; args: checksum byte, 32-bit cmd arg, cmd number
|
||||
SENDCMD_0:
|
||||
; clear RX FIFO first
|
||||
LOADCP CLEAR_RX_FIFO
|
||||
CALL
|
||||
|
||||
;LOADC '>'
|
||||
;LOADCP CONOUT
|
||||
;CALL
|
||||
|
||||
; cmd byte is at TOS at this point
|
||||
LOADC $40 ; or in start of frame bit
|
||||
OR
|
||||
LOADCP _SENDBYTE
|
||||
CALL
|
||||
; cmd arg is at TOS now
|
||||
LOADCP _SENDWORD
|
||||
CALL
|
||||
; checksum byte is at TOS now
|
||||
LOADCP _SENDBYTE
|
||||
CALL
|
||||
|
||||
LOADCP _XCVR_ENABLE ; enable transceiver last,
|
||||
CALL ; a complete command should
|
||||
RET ; fit into the tx fifo
|
||||
|
||||
; send ACMD and receive a 1-byte-response (R1)
|
||||
; args: checksum byte, 32-bit cmd arg, ACMD number
|
||||
; returns: received byte or -1 if first response byte
|
||||
; indicated an error
|
||||
SENDACMD_R1:
|
||||
LOADC $0
|
||||
LOADC $0
|
||||
LOADC 55 ; send CMD55
|
||||
LOADCP SENDCMD_R1
|
||||
CALL
|
||||
|
||||
LOADC 1 ; 1 = idle state, no errors
|
||||
CMP NE
|
||||
CBRANCH.NZ SENDACMD_ERR
|
||||
|
||||
; pass our args to SENDCMD_R1
|
||||
LOADCP SENDCMD_R1
|
||||
CALL
|
||||
RET
|
||||
|
||||
SENDACMD_ERR:
|
||||
LOADCP -1
|
||||
RET
|
||||
|
||||
; send a command and receive a 4+1-byte-response (R7)
|
||||
; args: checksum byte, 32-bit cmd arg, cmd number
|
||||
; returns: received word or -1 if first response byte
|
||||
; indicated an error
|
||||
|
||||
SENDCMD_R7:
|
||||
; send the command
|
||||
LOADCP SENDCMD_0
|
||||
CALL
|
||||
|
||||
;LOADC '7'
|
||||
;LOADCP CONOUT
|
||||
;CALL
|
||||
|
||||
LOADCP _RCVBYTE
|
||||
CALL
|
||||
|
||||
LOADCP _RCVWORD
|
||||
CALL
|
||||
|
||||
;terminate command (/cs high, disable clock)
|
||||
LOADCP _SPIENDCMD
|
||||
CALL
|
||||
|
||||
SWAP ; swap 1st response byte with received word
|
||||
LOADC %011111110 ; check for any error flags
|
||||
AND
|
||||
CBRANCH.Z SENDCMD_R7_NOERR
|
||||
DROP
|
||||
LOADC -1
|
||||
SENDCMD_R7_NOERR:
|
||||
RET
|
||||
|
||||
; send a word as 4 bytes, msb first
|
||||
_SENDWORD:
|
||||
DUP ; remember original value for later
|
||||
|
||||
BROT ; rotate msb to lsb (byte 0)
|
||||
LOADC 255
|
||||
AND.S0 ; isolate byte, keep previous value
|
||||
LOADCP _SENDBYTE
|
||||
CALL
|
||||
|
||||
BROT ; byte 1
|
||||
LOADC 255
|
||||
AND.S0
|
||||
LOADCP _SENDBYTE
|
||||
CALL
|
||||
|
||||
BROT ; byte 2
|
||||
LOADC 255
|
||||
AND
|
||||
LOADCP _SENDBYTE
|
||||
CALL
|
||||
|
||||
; byte 3 is already on the stack
|
||||
LOADC 255
|
||||
AND
|
||||
LOADCP _SENDBYTE
|
||||
CALL
|
||||
|
||||
RET
|
||||
|
||||
; send multiple 4-byte-words
|
||||
; args: number of words, pointer to buffer
|
||||
_SENDWORDS:
|
||||
FPADJ -4
|
||||
STORE 0 ; store pointer arg into local variable
|
||||
; keep counter on stack
|
||||
_SENDWORDS_LP:
|
||||
LOAD 0 ; load buf pointer
|
||||
DUP ; duplicate it
|
||||
INC 4 ; increment pointer
|
||||
STORE 0 ; and store it back
|
||||
LOADI ; load from previously duped pointer
|
||||
LOADCP _SENDWORD
|
||||
CALL ; send a word
|
||||
|
||||
DEC 1 ; decrement word counter
|
||||
DUP
|
||||
CBRANCH.NZ _SENDWORDS_LP ; if not null, loop
|
||||
DROP ; remove counter value
|
||||
|
||||
FPADJ 4
|
||||
RET
|
||||
|
||||
; receive multiple 4-byte-words and store into
|
||||
; memory buffer
|
||||
; args: number of words, pointer to buffer
|
||||
_RCVWORDS:
|
||||
FPADJ -4
|
||||
STORE 0 ; store pointer arg into local variable
|
||||
; keep counter on stack
|
||||
_RCVWORDS_LP:
|
||||
LOAD 0 ; load buf pointer for STOREI
|
||||
LOADCP _RCVWORD
|
||||
CALL ; receive a word
|
||||
STOREI 4 ; store to buf with postincrement
|
||||
STORE 0 ; store pointer variable
|
||||
|
||||
DEC 1 ; decrement word counter
|
||||
DUP
|
||||
CBRANCH.NZ _RCVWORDS_LP ; if not null, loop
|
||||
DROP ; remove counter value
|
||||
|
||||
FPADJ 4
|
||||
RET
|
||||
|
||||
; receive 4 bytes, return as word
|
||||
_RCVWORD:
|
||||
LOADCP _RCVBYTE ; receive first byte
|
||||
CALL
|
||||
BROT ; rotate byte to left
|
||||
|
||||
LOADCP _RCVBYTE ; receive second byte
|
||||
CALL
|
||||
OR ; or first and second byte together
|
||||
BROT ; rotate 1st + 2nd to left
|
||||
|
||||
LOADCP _RCVBYTE ; receive third byte
|
||||
CALL
|
||||
OR
|
||||
BROT
|
||||
|
||||
LOADCP _RCVBYTE ; receive fourth byte
|
||||
CALL
|
||||
OR
|
||||
RET
|
||||
|
||||
_XCVR_ENABLE:
|
||||
LOADC SPIREG
|
||||
LOADCP SPI_TX_FLAGS
|
||||
LOADI
|
||||
STOREI
|
||||
DROP
|
||||
RET
|
||||
|
||||
; send a byte
|
||||
; args: byte to be sent
|
||||
_SENDBYTE:
|
||||
LOADC SPIREG
|
||||
LOADI ; load spi io register
|
||||
LOADCP SPI_TX_RDY
|
||||
AND ; check tx_rdy bit
|
||||
CBRANCH.Z _SENDBYTE ; if not set, loop
|
||||
|
||||
LOADC SPI_TX_WR ; TX_WR bit
|
||||
OR ; OR in byte to be send
|
||||
|
||||
LOADC SPIREG
|
||||
SWAP ; swap value and addr for STOREI
|
||||
STOREI ; store word (flags + data) to io register
|
||||
DROP ; remove STOREI result
|
||||
|
||||
RET
|
||||
|
||||
; receive a byte. receiver must be enabled.
|
||||
; returns: received byte
|
||||
_RCVBYTE:
|
||||
LOADC SPIREG
|
||||
LOADI ; load spi io register
|
||||
LOADC SPI_RX_AVAIL
|
||||
AND.S0 ; check rx_avail bit, keep original value
|
||||
CBRANCH.NZ RECV_GOTIT
|
||||
DROP ; rx_avail not set, remove register value and loop
|
||||
BRANCH _RCVBYTE
|
||||
RECV_GOTIT:
|
||||
LOADC SPIREG
|
||||
LOADC SPI_RX_RD ; remove one byte from rx fifo
|
||||
STOREI
|
||||
DROP
|
||||
|
||||
LOADC 255
|
||||
AND ; keep bits 7-0
|
||||
RET
|
||||
|
||||
SPI_TX_FLAGS: .WORD SPI_CTRL_WRITE + SPI_TXRX_EN + SPI_RX_FILTER_EN
|
||||
SPI_IDLE_FLAGS: .WORD SPI_CTRL_WRITE
|
||||
|
||||
.CPOOL
|
||||
|
||||
CSD_BUF: .BLOCK 4
|
||||
CARD_BUF: .BLOCK 128
|
||||
|
||||
285
lib/stdlib.inc
Normal file
285
lib/stdlib.inc
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
(* Copyright 2021-2024 Sebastian Lederer. See the file LICENSE.md for details *)
|
||||
{ const pi = 3.14169253; }
|
||||
const MaxInt = 2147483647;
|
||||
|
||||
const MaxVolumes = 32;
|
||||
DefaultBufSize = 4096;
|
||||
DefaultBufBlocks = 8;
|
||||
DirSlotSize = 64;
|
||||
|
||||
const IONoError = 0;
|
||||
IOFileNotFound = 1;
|
||||
IOVolNotFound = 2;
|
||||
IOPathInvalid = 3;
|
||||
IOFileExists = 4;
|
||||
IOFileClosed = 5;
|
||||
IOSeekInvalid = 6;
|
||||
IONoSpace = 7;
|
||||
IOReadOnly = 8;
|
||||
IOInvalidOp = 9;
|
||||
IOInvalidFormat = 10;
|
||||
IOUserIntr = 11;
|
||||
IOMaxErr = 11;
|
||||
|
||||
const PArgMax = 7;
|
||||
|
||||
type IOBlock = array [0..127] of integer;
|
||||
type IOBuffer = array [0..7] of IOBlock;
|
||||
|
||||
type filetype = (IOChannel, IODiskFile);
|
||||
type filemode = (ModeReadonly, ModeCreate, ModeModify, ModeOverwrite, ModeAppend);
|
||||
type file = record
|
||||
mode: filemode;
|
||||
lastError: integer;
|
||||
errorAck: boolean;
|
||||
ateoln:boolean;
|
||||
case typ:filetype of
|
||||
IOChannel: (channelid:integer;
|
||||
bufchar:char; buflen:integer;
|
||||
ateof:boolean;
|
||||
noecho:boolean; (* read chars are not echoed *)
|
||||
raw:boolean; (* turn off backspace processing on input, CR processing on output *)
|
||||
nointr: boolean); (* turn off keyboard interrupt character processing *)
|
||||
|
||||
IODiskFile: (volumeid: integer;fileno: integer; filpos:integer; bufStart:integer;
|
||||
size:integer; sizeExtents:integer;
|
||||
bufBlocks, extentBlocks:integer;
|
||||
changed: boolean;
|
||||
buffer: ^IOBuffer;
|
||||
bufpos: integer;
|
||||
bufsize: integer;
|
||||
needsflush: boolean;
|
||||
);
|
||||
end;
|
||||
type text = file;
|
||||
|
||||
type fscanmode = (ScanInteger, ScanReal, ScanString);
|
||||
|
||||
type filenamestr = string[32];
|
||||
type pathnamestr = string[68];
|
||||
type volumenamestr = string[32];
|
||||
|
||||
type PartFlags = set of (PartEnabled, PartBoot, PartLast, PartPhysical, PartDefault);
|
||||
|
||||
type Partition = record
|
||||
name: volumenamestr;
|
||||
flags: PartFlags;
|
||||
startBlock: integer;
|
||||
blocks: integer;
|
||||
extentSize: integer; (* size of an extent in bytes, power of two > 512 *)
|
||||
dirSize: integer; (* number of directory slots *)
|
||||
bootBlocks: integer;
|
||||
end;
|
||||
|
||||
type PartitionTableBlock = array[0..7] of Partition;
|
||||
|
||||
type Volume = record
|
||||
part: Partition;
|
||||
deviceId: integer;
|
||||
partitionId: integer;
|
||||
startSlot: integer; (* first directory slot known to be in use *)
|
||||
freeSlot: integer; (* a directory slot that is probably free *)
|
||||
(* dirFile: ^file; (* pseudo-file for accessing the directory *)
|
||||
dirCache: ^DirBlock;
|
||||
cachedBlock: integer; (* cached volume block number in dirCache *)
|
||||
cacheDirty: boolean;
|
||||
openFilesCount: integer;
|
||||
end;
|
||||
|
||||
type DirSlotFlags = set of (SlotFree, SlotReserved, SlotDeleted, SlotEndScan, SlotFirst, SlotExtent, SlotReadonly);
|
||||
|
||||
type Timestamp = integer;
|
||||
|
||||
type DirectorySlot = record
|
||||
name: filenamestr; (* the name of the file *)
|
||||
flags: DirSlotFlags; (* see above *)
|
||||
sizeBytes: integer; (* the size of the file in bytes *)
|
||||
createTime: Timestamp; (* creation time of the file *)
|
||||
modTime: Timestamp; (* time of last file modification *)
|
||||
generation: integer; (* increased each time a file is overwritten *)
|
||||
owner: integer; (* unused *)
|
||||
end;
|
||||
|
||||
DirBlock = array [0..7] of DirectorySlot;
|
||||
|
||||
type PArgVec = array[0..PArgMax] of string;
|
||||
|
||||
type DateTime = record
|
||||
year:integer;
|
||||
month: 1..12;
|
||||
day: 1..31;
|
||||
hours: 0..23;
|
||||
minutes: 0..59;
|
||||
seconds: 0..59;
|
||||
end;
|
||||
|
||||
var input,output:file external;
|
||||
var DefaultVolumeId:integer external; (* do we need this here? *)
|
||||
VolumeTable: array [1..MaxVolumes] of Volume external; (* and this *)
|
||||
VolumeCount: integer external; (* and this *)
|
||||
|
||||
DefaultVolume: volumenamestr external;
|
||||
|
||||
SysBootTicks, SysLastTicks:integer external;
|
||||
SysClock:DateTime external;
|
||||
|
||||
(* from graphics.s *)
|
||||
PROCEDURE DRAWLINE(x1,y1,x2,y2, color:INTEGER); EXTERNAL;
|
||||
PROCEDURE PUTPIXEL(x,y, color:INTEGER); EXTERNAL;
|
||||
PROCEDURE CLEARGRAPHICS; EXTERNAL;
|
||||
PROCEDURE INITGRAPHICS; EXTERNAL;
|
||||
PROCEDURE SETPALETTE(slot, color:INTEGER);EXTERNAL;
|
||||
PROCEDURE PUTSCREEN(VAR pixeldata: ARRAY [0..31999] OF INTEGER); EXTERNAL;
|
||||
|
||||
function conin():char; external;
|
||||
procedure conout(c:char); external;
|
||||
FUNCTION CONAVAIL:BOOLEAN; EXTERNAL;
|
||||
PROCEDURE WAIT1MSEC; EXTERNAL;
|
||||
function upcase(aChar:char):char; external;
|
||||
function getticks():integer; external;
|
||||
|
||||
(* from float32.s *)
|
||||
function shiftfloat32(aReal:real; shiftCount:integer):real; external;
|
||||
function getfloat32exp(aReal:real):integer; external;
|
||||
|
||||
(* from runtime.s *)
|
||||
FUNCTION LENGTH(s:STRING):INTEGER; EXTERNAL;
|
||||
FUNCTION MAXLENGTH(s:STRING):INTEGER; EXTERNAL;
|
||||
procedure appendchar(var s:string; aChar:char); external;
|
||||
procedure strmoveup(var s:string;index,length,delta:integer); external;
|
||||
procedure strmovedown(var s:string;index,length,delta:integer); external;
|
||||
procedure RuntimeError(var s:string); external;
|
||||
|
||||
(* from stdlib *)
|
||||
function copy(s:string;index,count:integer):string; external;
|
||||
procedure insert(ins: string; var dest: string; position:integer); external;
|
||||
procedure delete(var s:string; from:integer; count:integer); external;
|
||||
function pos(substr:string;var s:string):integer; external;
|
||||
function pwroften(exp:integer):real; external;
|
||||
function exp(exponent:real):real; external;
|
||||
function ln(power:real):real; external;
|
||||
function sqrt(n:real):real; external;
|
||||
function floor(x:real):integer; external;
|
||||
function round(x:real):integer; external;
|
||||
function sin(x:real):real; external;
|
||||
function cos(x:real):real; external;
|
||||
function arctan(x:real):real; external;
|
||||
function tan(x:real):real; external;
|
||||
function cotan(x:real):real; external;
|
||||
|
||||
procedure fillchar(var s:string; startpos,count:integer; theChar:char); external;
|
||||
|
||||
procedure cardinitv2; external;
|
||||
function cardsize:integer; external;
|
||||
function cardchanged:boolean; external;
|
||||
|
||||
procedure readpartblk(blkno:integer;var partblk:PartitionTableBlock;
|
||||
var error:integer;devid: integer); external;
|
||||
procedure readdirblk(blkno:integer;var dirblk:DirBlock;
|
||||
var error:integer;devid: integer); external;
|
||||
procedure readblock(blkno:integer;var buf:IOBlock;
|
||||
var error:integer; devid: integer); external;
|
||||
|
||||
procedure writedirblk(blkno:integer;var dirblk:DirBlock;
|
||||
var error:integer;devid: integer); external;
|
||||
procedure writepartblk(blkno:integer;var partblk:PartitionTableBlock;
|
||||
var error:integer;devid: integer); external;
|
||||
procedure writeblock(blkno:integer;var buf:IOBlock;
|
||||
var error:integer; devid: integer); external;
|
||||
|
||||
procedure copybuf(dest:^IOBuffer;destOffset:integer; src:^IOBuffer; srcOffset:integer; length: integer); external;
|
||||
function readfschar(var f:file):char; external;
|
||||
procedure writefschar(var f:file; aChar:char); external;
|
||||
procedure writefsstring(var f:file; var s:string); external;
|
||||
|
||||
procedure conoutw(w:integer); external;
|
||||
function coninw():integer; external;
|
||||
|
||||
procedure SetDefaultVolume(volname:volumenamestr); external;
|
||||
procedure addPartitions(devid:integer; var partblk:PartitionTableBlock; var isLast:boolean); external;
|
||||
procedure readPartitions(devid: integer); external;
|
||||
procedure initDevices; external;
|
||||
procedure readdevice(deviceId:integer;blockNo:integer;var buf:IOBlock; var error:integer); external;
|
||||
procedure writedevice(deviceId:integer;blockNo:integer;var buf:IOBlock; var error:integer); external;
|
||||
procedure readvolumeblks(volumeid:integer; destbuf:^iobuffer; blkno:integer; blkCount: integer; var error:integer);
|
||||
external;
|
||||
procedure writevolumeblks(volumeid:integer; srcbuf:^iobuffer; blkno:integer; blkCount: integer; var error:integer);
|
||||
external;
|
||||
function findvolume(name:string):integer; external;
|
||||
procedure openvolumeid(volid:integer); external;
|
||||
procedure closevolumeid(volid:integer); external;
|
||||
function IOResult(var fil:file):integer; external;
|
||||
function ErrorStr(err:integer):string; external;
|
||||
function eof(var fil:file):boolean; external;
|
||||
function eoln(var fil:file):boolean; external;
|
||||
procedure readfs(var fil:file; destbuf:^IOBuffer; len:integer); external;
|
||||
procedure flushfile(var fil:file); external;
|
||||
procedure seek(var fil:file; position:integer); external;
|
||||
function filepos(var fil:file):integer; external;
|
||||
function filesize(var fil:file):integer; external;
|
||||
procedure extendfile(var fil:file; newSize:integer); external;
|
||||
procedure writefs(var fil:file; srcbuf:^IOBuffer; len:integer); external;
|
||||
procedure close(var aFile:file); external;
|
||||
procedure readdirnext(volid:integer; var index:integer; var dirslot:DirectorySlot; var error:integer); external;
|
||||
procedure readdirfirst(volid:integer; var index:integer; var dirslot:DirectorySlot; var error:integer); external;
|
||||
function charpos(searchChar:char; var s:string):integer; external;
|
||||
procedure rename(oldname:filenamestr; newname:filenamestr; var error:integer); external;
|
||||
procedure erase(name:pathnamestr; var error:integer); external;
|
||||
function readchannel(var f:file):char; external;
|
||||
procedure writechannel(var f:file; aChar:char); external;
|
||||
function freadchar(var f:file):char; external;
|
||||
procedure fwritechar(aChar:char; var f:file); external;
|
||||
procedure fwritestring(var aString:string; var f:file; w:integer); external;
|
||||
procedure fwriteint(v:integer; var f:file; w:integer); external;
|
||||
procedure fwritereal(v:real; var f:file; w,d:integer); external;
|
||||
procedure pushback(var aFile:file; aChar:char); external;
|
||||
procedure skipeoln(var aFile:file); external;
|
||||
procedure fscanbuf(var aFile:file; mode: fscanmode; var buf:string); external;
|
||||
procedure freadint(var v:integer;var f:file); external;
|
||||
procedure freadreal(var v:real;var f:file); external;
|
||||
|
||||
procedure openchannel(name:filenamestr; var f:file; mode:filemode; var error:integer); external;
|
||||
procedure open(var f:file; name:pathnamestr; mode: filemode); external;
|
||||
procedure noecho(var f:file; noecho:boolean; var old:boolean); external;
|
||||
|
||||
procedure intstr(v:integer; fieldWith:integer; var rbuf:string);
|
||||
external;
|
||||
procedure realstr(x:real; w, d: integer; var s: string[30]); external;
|
||||
procedure intval(s:string; var value,code:integer); external;
|
||||
procedure realval(s:string; var value:real;var code:integer); external;
|
||||
function isdigit(aChar:char):boolean; external;
|
||||
function iswhite(aChar:char):boolean; external;
|
||||
|
||||
procedure halt; external;
|
||||
|
||||
function random:integer; external;
|
||||
procedure randomize; external;
|
||||
|
||||
(* from stdterm.inc *)
|
||||
procedure ClrScr; external;
|
||||
procedure ClrEol; external;
|
||||
procedure CrtInit; external;
|
||||
procedure GotoXY(x,y:integer); external;
|
||||
procedure InsLine; external;
|
||||
procedure DelLine; external;
|
||||
procedure GetCursorPos(var x,y:integer); external;
|
||||
procedure GetTermSize(var maxx,maxy:integer); external;
|
||||
procedure TextColor(col:integer); external;
|
||||
procedure TextBackground(bgcol:integer); external;
|
||||
procedure TextDefault; external;
|
||||
|
||||
procedure PTerm; external; (* from runtime.s *)
|
||||
procedure PExec(prgfile:pathnamestr; var args:PArgVec; argCount:integer;var error:integer); external;
|
||||
procedure PExec2(prgfile:pathnamestr; arg1:string; var error:integer); external;
|
||||
procedure PExec3(prgfile:pathnamestr; arg1, arg2:string; var error:integer); external;
|
||||
function ParamStr(i:integer):string; external;
|
||||
function ParamCount():integer; external;
|
||||
|
||||
procedure SetShellCmd(cmd:string[40];arg:integer); external;
|
||||
|
||||
function GetTime:DateTime; external;
|
||||
function TimeStr(d:DateTime;showSeconds:boolean):string; external;
|
||||
function DateStr(d:DateTime):string; external;
|
||||
function GetTimestamp(var d:DateTime):integer; external;
|
||||
function GetDateTime(ts:Timestamp):DateTime; external;
|
||||
procedure delay(ms:integer); external;
|
||||
2710
lib/stdlib.pas
Normal file
2710
lib/stdlib.pas
Normal file
File diff suppressed because it is too large
Load diff
57
lib/stdterm.inc
Normal file
57
lib/stdterm.inc
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
(* Copyright 2021-2024 Sebastian Lederer. See the file LICENSE.md for details *)
|
||||
procedure ClrScr;
|
||||
begin
|
||||
write(#27, '[2J');
|
||||
write(#27, '[H');
|
||||
end;
|
||||
|
||||
procedure ClrEol;
|
||||
begin
|
||||
write(#27, '[K');
|
||||
end;
|
||||
|
||||
procedure CrtInit;
|
||||
begin
|
||||
write(#27, 'c');
|
||||
end;
|
||||
|
||||
procedure GotoXY(x,y:integer);
|
||||
begin
|
||||
write(#27,'[', y, ';', x, 'H');
|
||||
end;
|
||||
|
||||
procedure InsLine;
|
||||
begin
|
||||
write(#27,'[L');
|
||||
end;
|
||||
|
||||
procedure DelLine;
|
||||
begin
|
||||
write(#27,'[M');
|
||||
end;
|
||||
|
||||
procedure GetCursorPos(var x,y:integer); external; (* from corelib.s *)
|
||||
|
||||
procedure GetTermSize(var maxx,maxy:integer);
|
||||
var x,y:integer;
|
||||
begin
|
||||
GetCursorPos(x,y);
|
||||
GotoXY(9999,9999);
|
||||
GetCursorPos(maxx,maxy);
|
||||
GotoXY(x,y);
|
||||
end;
|
||||
|
||||
procedure TextColor(col:integer);
|
||||
begin
|
||||
write(#27,'[38;5;',col,'m');
|
||||
end;
|
||||
|
||||
procedure TextBackground(bgcol:integer);
|
||||
begin
|
||||
write(#27,'[48;5;',bgcol,'m');
|
||||
end;
|
||||
|
||||
procedure TextDefault;
|
||||
begin
|
||||
write(#27,'[0m');
|
||||
end;
|
||||
17
pcomp/.vscode/tasks.json
vendored
Normal file
17
pcomp/.vscode/tasks.json
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "pcomp",
|
||||
"type": "shell",
|
||||
"command": "fpc -Mobjfpc -gl pcomp.pas",
|
||||
"problemMatcher": [],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
1620
pcomp/emit.pas
Normal file
1620
pcomp/emit.pas
Normal file
File diff suppressed because it is too large
Load diff
95
pcomp/float32+.pas
Normal file
95
pcomp/float32+.pas
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
(* Copyright 2021-2024 Sebastian Lederer. See the file LICENSE.md for details *)
|
||||
function encodefloat32(r:real):integer;
|
||||
var intpart:real;
|
||||
fract: real;
|
||||
exponent:integer;
|
||||
sign:integer;
|
||||
i:integer;
|
||||
digit, bitpos:integer;
|
||||
intlength,fractlength:integer;
|
||||
intbin:integer;
|
||||
fractbin:integer;
|
||||
floatbin:integer;
|
||||
begin
|
||||
intbin := 0; fractbin := 0; floatbin := 0;
|
||||
|
||||
if r<0 then
|
||||
begin
|
||||
r := abs(r);
|
||||
sign := 1;
|
||||
end
|
||||
else
|
||||
sign := 0;
|
||||
|
||||
if r = 0.0 then
|
||||
begin
|
||||
intpart := 0.0;
|
||||
fract := 0.0;
|
||||
intlength := 0;
|
||||
fractlength := 0;
|
||||
intbin := 0;
|
||||
fractbin := 0;
|
||||
floatbin := 0;
|
||||
end
|
||||
else
|
||||
begin
|
||||
intpart := r;
|
||||
fract := frac(r);
|
||||
exponent := floor(log2(intpart));
|
||||
|
||||
intlength := exponent+1;
|
||||
fractlength := wordbits - intlength - Float32ExpBits - 1;
|
||||
end;
|
||||
(* FIXME: log2 gives division by zero on zero arg *)
|
||||
|
||||
|
||||
(* process bits before the point *)
|
||||
for i := 1 to intlength do
|
||||
begin
|
||||
(* digit := round(intpart mod 2.0); *)
|
||||
(* calculate real remainder in a portable way *)
|
||||
digit := floor(intpart - 2 * Int(intpart / 2));
|
||||
|
||||
(* if we used up all the bits in the fraction part of
|
||||
the float32 encoding, shift everything right
|
||||
and put bit at the top *)
|
||||
if i > Float32FractBits then
|
||||
begin
|
||||
bitpos := Float32FractBits-1;
|
||||
intbin := intbin shr 1;
|
||||
end
|
||||
else
|
||||
bitpos := i - 1;
|
||||
|
||||
if digit > 0 then intbin := intbin + (1 << bitpos);
|
||||
|
||||
intpart := intpart / 2.0;
|
||||
end;
|
||||
|
||||
(* limit the integer bits *)
|
||||
if intlength > Float32FractBits then intlength := Float32FractBits;
|
||||
|
||||
(* process bits after the point, if we have any bits left *)
|
||||
if fractlength > 0 then
|
||||
begin
|
||||
for i := 1 to fractlength do
|
||||
begin
|
||||
fract := fract * 2;
|
||||
digit := trunc(fract) and 1;
|
||||
fractbin := (fractbin shl 1) + digit;
|
||||
end;
|
||||
|
||||
end;
|
||||
|
||||
floatbin := (intbin << (Float32FractBits - intlength)) + fractbin;
|
||||
|
||||
if floatbin = 0 then (* if mantissa is zero, return a clean zero value *)
|
||||
encodefloat32 := 0
|
||||
else
|
||||
begin
|
||||
exponent := exponent + Float32ExpBias;
|
||||
if (exponent > Float32ExpMax) or (exponent < 0) then
|
||||
errorExit2('float exponent overflow','');
|
||||
encodefloat32 := (sign shl (wordBits-1)) + (floatbin << Float32ExpBits) + exponent;
|
||||
end;
|
||||
end;
|
||||
2
pcomp/float32+tdr.pas
Normal file
2
pcomp/float32+tdr.pas
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
(* Copyright 2021-2024 Sebastian Lederer. See the file LICENSE.md for details *)
|
||||
function encodefloat32(r:real):integer; external;
|
||||
304
pcomp/libgen.pas
Normal file
304
pcomp/libgen.pas
Normal file
|
|
@ -0,0 +1,304 @@
|
|||
(* Copyright 2021-2024 Sebastian Lederer. See the file LICENSE.md for details *)
|
||||
program libgen;
|
||||
|
||||
const shortcutChar = '`';
|
||||
firstShCChar = 'A';
|
||||
lastShCChar = 'i';
|
||||
|
||||
OutfileSuffix = '.lib';
|
||||
|
||||
{$I 'platfile-types+.pas'}
|
||||
|
||||
type
|
||||
InsString = string[24];
|
||||
|
||||
var shortcuts:array [firstShCChar..lastShCChar] of InsString;
|
||||
infile:TextFile;
|
||||
outfile:TextFile;
|
||||
infileName:string;
|
||||
outfileName:string;
|
||||
lineCount:integer;
|
||||
|
||||
procedure errorExit2(reason:string;arg:string); forward;
|
||||
|
||||
{$I 'platfile+.pas'}
|
||||
|
||||
procedure errorExit2(reason:string;arg:string);
|
||||
begin
|
||||
writeln;
|
||||
writeln('Error: ', reason, ' ', arg);
|
||||
halt;
|
||||
end;
|
||||
|
||||
procedure addShortcut(ch:char; dest:InsString);
|
||||
begin
|
||||
shortcuts[ch] := dest;
|
||||
end;
|
||||
|
||||
function findShortcut(ins:InsString):char;
|
||||
var ch:char;
|
||||
begin
|
||||
findShortCut := #0;
|
||||
|
||||
for ch := firstShCChar to lastShCChar do
|
||||
begin
|
||||
if shortcuts[ch] = ins then
|
||||
begin
|
||||
findShortcut := ch;
|
||||
break;
|
||||
end;
|
||||
end;
|
||||
{ if findShortCut = #0 then writeln('findShortcut:#0'); }
|
||||
end;
|
||||
|
||||
procedure initShortcuts;
|
||||
begin
|
||||
addShortcut('A', 'ADD');
|
||||
addShortcut('B', 'BRANCH');
|
||||
addShortcut('C', 'CALL');
|
||||
addShortcut('D', 'DUP');
|
||||
addShortcut('E', 'LOADREL');
|
||||
addShortcut('F', 'LOAD');
|
||||
addShortcut('G', 'LOADREG');
|
||||
addShortcut('H', 'SHL');
|
||||
addShortcut('I', 'LOADI');
|
||||
addShortcut('J', 'JUMP');
|
||||
addShortcut('K', 'LOADC');
|
||||
addShortcut('L', 'LOADCP');
|
||||
addShortcut('M', 'STORE');
|
||||
addShortcut('N', 'NIP');
|
||||
addShortcut('O', 'OR');
|
||||
addShortcut('P', 'DROP');
|
||||
(* Q is unused *)
|
||||
addShortcut('R', 'RET');
|
||||
addShortcut('S', 'STOREI');
|
||||
addShortcut('T', 'NOT');
|
||||
addShortcut('U', 'CMPU');
|
||||
addShortcut('V', 'OVER');
|
||||
addShortcut('W', 'SWAP');
|
||||
addShortcut('X', 'XOR');
|
||||
(* Y is ununsed *)
|
||||
addShortcut('Z', 'SUB');
|
||||
addShortcut('a', 'AND');
|
||||
addShortcut('b', 'CBRANCH');
|
||||
addShortcut('c', 'CMP');
|
||||
addShortcut('d', 'DEC');
|
||||
(* e is unused *)
|
||||
addShortcut('f', 'FPADJ');
|
||||
addShortcut('g', 'STOREREG');
|
||||
addShortcut('h', 'SHR');
|
||||
addShortcut('i', 'INC');
|
||||
end;
|
||||
|
||||
procedure processLine(var linebuf:string);
|
||||
var labelEnd:integer;
|
||||
dotPos:integer;
|
||||
endPos:integer;
|
||||
insStart:integer;
|
||||
insEnd:integer;
|
||||
labelBuf:string;
|
||||
insBuf:string;
|
||||
restBuf:string;
|
||||
short:char;
|
||||
|
||||
procedure scanLine;
|
||||
var c:char;
|
||||
i:integer;
|
||||
begin
|
||||
labelEnd := 0;
|
||||
dotPos := 0;
|
||||
endPos := 0;
|
||||
insStart := 0;
|
||||
insEnd := 0;
|
||||
|
||||
i := 1;
|
||||
for c in linebuf do
|
||||
begin
|
||||
if (labelEnd = 0) and (c = ':') then
|
||||
begin
|
||||
insStart := 0;
|
||||
insEnd := 0;
|
||||
labelEnd := i;
|
||||
end
|
||||
else
|
||||
if (dotPos = 0) and (c = '.') then
|
||||
begin
|
||||
insEnd := i - 1;
|
||||
dotPos := i;
|
||||
end
|
||||
else
|
||||
if c = ';' then break
|
||||
else
|
||||
if c in [ ' ', #9 ] then
|
||||
begin
|
||||
if (insStart <> 0 ) and (insEnd = 0) then
|
||||
insEnd := i - 1;
|
||||
end
|
||||
else
|
||||
if c in [ '''', '"' ] then
|
||||
begin
|
||||
(* we do not want to deal with string quoting,
|
||||
so if we encounter some quotes,
|
||||
just do nothing *)
|
||||
insStart := 0;
|
||||
insEnd := 0;
|
||||
labelEnd := 0;
|
||||
endPos := length(linebuf);
|
||||
break;
|
||||
end
|
||||
else
|
||||
begin
|
||||
if insStart = 0 then
|
||||
insStart := i;
|
||||
endPos := i;
|
||||
{ writeln('c:', c, ' i:', i, ' insStart:', insStart); }
|
||||
end;
|
||||
|
||||
i := i + 1;
|
||||
end;
|
||||
if insEnd = 0 then insEnd := endPos;
|
||||
end;
|
||||
|
||||
begin
|
||||
if length(linebuf) > 0 then
|
||||
if linebuf[1] <> '%' then
|
||||
begin
|
||||
scanLine;
|
||||
if labelEnd > 0 then
|
||||
labelBuf := copy(linebuf,1,labelEnd)
|
||||
else
|
||||
labelBuf := '';
|
||||
|
||||
if insStart > 0 then
|
||||
insBuf := copy(linebuf, insStart, insEnd - insStart + 1)
|
||||
else
|
||||
insBuf := '';
|
||||
|
||||
if endPos <> insEnd then
|
||||
restBuf := copy(linebuf, insEnd + 1, endPos - insEnd + 1)
|
||||
else
|
||||
restBuf := '';
|
||||
{
|
||||
writeln('ins ', insBuf);
|
||||
writeln('label ', labelBuf);
|
||||
writeln('rest ', restBuf);
|
||||
writeln('insStart ', insStart);
|
||||
writeln('insEnd ', insEnd);
|
||||
writeln('dotPos ', dotPos);
|
||||
writeln('endPos ', endPos);
|
||||
}
|
||||
short := #0;
|
||||
if length(insBuf) > 0 then
|
||||
begin
|
||||
(* if we found an instruction, try to find a shortcut *)
|
||||
short := findShortcut(insBuf);
|
||||
|
||||
if short <> #0 then
|
||||
writeln(outfile, labelBuf, '`', short, restBuf)
|
||||
else
|
||||
(* if no shortcut, we still remove comments and whitespace *)
|
||||
writeln(outfile, labelBuf, ' ', insBuf, restBuf);
|
||||
end
|
||||
else
|
||||
(* no instruction found, probably a directive, so
|
||||
no change *)
|
||||
writeln(outfile, linebuf);
|
||||
end
|
||||
else
|
||||
writeln(outfile, linebuf);
|
||||
end;
|
||||
|
||||
procedure processAllLines;
|
||||
var linebuf:string;
|
||||
begin
|
||||
while not eof(infile) do
|
||||
begin
|
||||
readln(infile, linebuf);
|
||||
lineCount := lineCount + 1;
|
||||
if (lineCount and 255) = 1 then
|
||||
write(lineCount, ' lines', #13);
|
||||
processLine(linebuf);
|
||||
end;
|
||||
writeln(lineCount, ' lines');
|
||||
end;
|
||||
|
||||
procedure test;
|
||||
var buf:string;
|
||||
begin
|
||||
outfile := output;
|
||||
|
||||
buf := 'LABEL: SOMEINS.MOD1.MOD2 ARG ; a comment';
|
||||
processLine(buf);
|
||||
buf := ' SOMEINS.MOD1.MOD2 ARG ; a comment';
|
||||
processLine(buf);
|
||||
buf := ' LOADCP 1';
|
||||
processLine(buf);
|
||||
buf := ' JUMP';
|
||||
processLine(buf);
|
||||
buf := 'LABEL: FPADJ -20';
|
||||
processLine(buf);
|
||||
buf := 'LABEL: .BYTE ":;123"';
|
||||
processLine(buf);
|
||||
buf := 'LABEL: .LCBRANCH SOMEWHERE';
|
||||
processLine(buf);
|
||||
buf := 'LABEL: LOADC '';''';
|
||||
processLine(buf);
|
||||
end;
|
||||
|
||||
function changeSuffix(var fname:string):string;
|
||||
var dotPos:integer;
|
||||
found:boolean;
|
||||
begin
|
||||
found := false;
|
||||
|
||||
for dotPos := length(fname) downto 1 do
|
||||
if fname[dotPos] = '.' then
|
||||
begin
|
||||
found := true;
|
||||
break;
|
||||
end;
|
||||
|
||||
if found then
|
||||
changeSuffix := copy(fname,1, dotPos - 1) + OutfileSuffix
|
||||
else
|
||||
changeSuffix := fname + OutfileSuffix;
|
||||
end;
|
||||
|
||||
begin
|
||||
initShortcuts;
|
||||
|
||||
{ test;
|
||||
halt; }
|
||||
|
||||
outfileName := '';
|
||||
|
||||
case ParamCount of
|
||||
0: begin
|
||||
write('Source file: ');
|
||||
readln(infileName);
|
||||
end;
|
||||
1: infileName := ParamStr(1);
|
||||
2: begin
|
||||
infileName := ParamStr(1);
|
||||
outfileName := ParamStr(2);
|
||||
end
|
||||
else
|
||||
begin
|
||||
writeln('Invalid arguments.');
|
||||
halt;
|
||||
end;
|
||||
end;
|
||||
|
||||
if length(outfileName) = 0 then
|
||||
outfileName := changeSuffix(infileName);
|
||||
|
||||
writeln('Output file: ', outfileName);
|
||||
|
||||
openTextFile(infile, infileName);
|
||||
overwriteTextFile(outfile, outfileName);
|
||||
|
||||
processAllLines;
|
||||
|
||||
close(infile);
|
||||
close(outfile);
|
||||
end.
|
||||
111
pcomp/lsymgen.pas
Normal file
111
pcomp/lsymgen.pas
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
(* Copyright 2021-2024 Sebastian Lederer. See the file LICENSE.md for details *)
|
||||
program lsymgen;
|
||||
|
||||
const OutfileSuffix = '.lsym';
|
||||
|
||||
{$I 'platfile-types+.pas'}
|
||||
|
||||
var
|
||||
outfile:TextFile;
|
||||
infile:TextFile;
|
||||
lineno:integer;
|
||||
|
||||
procedure errorExit2(reason:string;arg:string); forward;
|
||||
|
||||
{$I 'platfile+.pas'}
|
||||
|
||||
procedure errorExit2(reason:string;arg:string);
|
||||
begin
|
||||
writeln;
|
||||
writeln('Error: ', reason, ' ', arg);
|
||||
writeln('at ', lineno);
|
||||
halt;
|
||||
end;
|
||||
|
||||
function rpos(c:char; var s:string):integer;
|
||||
var i:integer;
|
||||
begin
|
||||
for i := length(s) downto 1 do
|
||||
if s[i] = '.' then break;
|
||||
if i = 1 then
|
||||
rpos := 0
|
||||
else
|
||||
rpos := i;
|
||||
end;
|
||||
|
||||
function strcontains(var s:string; c:char):boolean;
|
||||
begin
|
||||
strcontains := pos(c, s) > 0;
|
||||
end;
|
||||
|
||||
function getOutfileName(infileName:string):string;
|
||||
var p:integer;
|
||||
begin
|
||||
p := rpos('.', infileName);
|
||||
if p > 1 then
|
||||
getOutfileName := copy(infileName, 1, p - 1)
|
||||
else
|
||||
getOutfileName := infileName;
|
||||
|
||||
getOutfileName := getOutfileName + OutfileSuffix;
|
||||
end;
|
||||
|
||||
procedure splitLine(var line:string; var addr:string; var name:string;
|
||||
var clean:boolean);
|
||||
var n,l:integer;
|
||||
begin
|
||||
n := pos(' ', line);
|
||||
|
||||
if n <= 1 then
|
||||
errorExit2('invalid syntax:', line);
|
||||
|
||||
addr := copy(line, 1, n - 1);
|
||||
|
||||
l := length(line);
|
||||
while (n < l) and (line[n] = ' ') do
|
||||
n := n + 1;
|
||||
|
||||
name := copy(line, n, l - n + 1);
|
||||
|
||||
|
||||
(* symbols starting with '!' are explicitly exported *)
|
||||
if name[1] = '!' then
|
||||
begin
|
||||
clean := true;
|
||||
name := copy(name, 2, length(name) - 1);
|
||||
end
|
||||
else
|
||||
clean := (not strcontains( name, '_')) and (name[1] <> '=');
|
||||
end;
|
||||
|
||||
procedure processFile(inpath,outpath:string);
|
||||
var line:string;
|
||||
addr,name:string;
|
||||
clean:boolean;
|
||||
begin
|
||||
lineno := 0;
|
||||
writeln('writing file ', outpath);
|
||||
|
||||
openTextFile(infile, inpath);
|
||||
|
||||
overwriteTextFile(outfile, outpath);
|
||||
|
||||
while not eof(infile) do
|
||||
begin
|
||||
readln(infile, line);
|
||||
splitLine(line, addr, name, clean);
|
||||
if clean then
|
||||
writeln(outfile, #9, '.EQU ', name, ' $', addr);
|
||||
end;
|
||||
close(infile);
|
||||
close(outfile);
|
||||
end;
|
||||
|
||||
begin
|
||||
if ParamCount > 0 then
|
||||
begin
|
||||
processFile(ParamStr(1), getOutfileName(ParamStr(1)));
|
||||
end
|
||||
else
|
||||
writeln('No file name given.');
|
||||
end.
|
||||
38
pcomp/make.bat
Normal file
38
pcomp/make.bat
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
del *.s
|
||||
del ..\lib\*.lib
|
||||
del ..\lib\stdlib.s
|
||||
|
||||
fpc -Mobjfpc -gl pcomp.pas
|
||||
fpc -gl sasm.pas
|
||||
fpc -gl lsymgen.pas
|
||||
|
||||
sasm ..\lib\coreloader.s
|
||||
lsymgen ..\lib\coreloader.sym
|
||||
py pcomp.py -n ..\lib\stdlib.pas
|
||||
libgen ..\lib\stdlib.s
|
||||
libgen ..\lib\runtime.s
|
||||
libgen ..\lib\float32.s
|
||||
|
||||
py pcomp.py sasm.pas
|
||||
py pcomp.py pcomp.pas
|
||||
py pcomp.py lsymgen.pas
|
||||
py pcomp.py libgen.pas
|
||||
|
||||
rem exit /b
|
||||
|
||||
py pcomp.py ..\progs\shell.pas
|
||||
py pcomp.py ..\progs\editor.pas
|
||||
py pcomp.py ..\progs\reclaim.pas
|
||||
py pcomp.py ..\progs\dumpdir.pas
|
||||
py pcomp.py ..\progs\partmgr.pas
|
||||
py pcomp.py ..\progs\xfer.pas
|
||||
|
||||
rem exit /b
|
||||
|
||||
py pcomp.py ..\tests\readtest.pas
|
||||
py pcomp.py ..\tests\readchartest.pas
|
||||
py pcomp.py ..\tests\timetest.pas
|
||||
py pcomp.py ..\tests\test133.pas
|
||||
py pcomp.py ..\examples\chase.pas
|
||||
py pcomp.py ..\tests\cchangetest.pas
|
||||
py pcomp.py ..\tests\tree.pas
|
||||
6452
pcomp/pcomp.pas
Normal file
6452
pcomp/pcomp.pas
Normal file
File diff suppressed because it is too large
Load diff
102
pcomp/pcomp.py
Normal file
102
pcomp/pcomp.py
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
#!/usr/bin/python3
|
||||
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
|
||||
# Copyright 2021-2024 Sebastian Lederer. See the file LICENSE.md for details
|
||||
|
||||
import sys
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
suffixes = [ '.teeny', '.pas' ]
|
||||
compiler = 'pcomp'
|
||||
#assembler = '..\sasm\sasm.py'
|
||||
assembler = 'sasm'
|
||||
emulator = 's4emu.py'
|
||||
|
||||
asm_include_path = '../lib'
|
||||
|
||||
def run_compiler(filename, opts):
|
||||
print("compiling {}...".format(filename))
|
||||
args = [compiler]
|
||||
args.extend(opts)
|
||||
args.append(filename)
|
||||
#print("args:",args)
|
||||
status = subprocess.call(args)
|
||||
if status != 0:
|
||||
sys.exit(2)
|
||||
|
||||
|
||||
def run_assembler(filename):
|
||||
print("assembling {}...".format(filename))
|
||||
args = [assembler]
|
||||
# args.extend([ '-I', asm_include_path])
|
||||
args.append(filename)
|
||||
status = subprocess.call(args)
|
||||
if status != 0:
|
||||
sys.exit(3)
|
||||
|
||||
|
||||
def run_emulator(filename, extra_args):
|
||||
args = ['py', emulator, '-a', '24576', filename ]
|
||||
args.extend(extra_args)
|
||||
status = subprocess.call(args)
|
||||
if status != 0:
|
||||
sys.exit(4)
|
||||
|
||||
|
||||
def get_compiler_options():
|
||||
comp_options = [ "-n", "-s", "-e", "-R", "-S", "-H" ]
|
||||
result = []
|
||||
while len(sys.argv) > 1 and sys.argv[1] in comp_options:
|
||||
result.append(sys.argv[1])
|
||||
if sys.argv[1] == "-H":
|
||||
sys.argv.pop(1)
|
||||
result.append(sys.argv[1])
|
||||
sys.argv.pop(1)
|
||||
# print("Compiler options:",result, sys.argv[1])
|
||||
return result
|
||||
|
||||
|
||||
def main():
|
||||
do_compile = True
|
||||
do_assemble = True
|
||||
do_emulator = False
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: {} <input file>".format(sys.argv[0]))
|
||||
sys.exit(1)
|
||||
|
||||
compiler_options = get_compiler_options()
|
||||
infilename = sys.argv[1]
|
||||
basename = infilename
|
||||
|
||||
if infilename.endswith('.s'):
|
||||
do_compile = False
|
||||
basename = infilename[:-2]
|
||||
elif infilename.endswith('.bin') or infilename.endswith('.prog'):
|
||||
do_compile = False
|
||||
do_assemble = False
|
||||
do_emulator = True
|
||||
basename = infilename[:-4]
|
||||
else:
|
||||
fname, suffix = os.path.splitext(infilename)
|
||||
if suffix in suffixes:
|
||||
print("#############",fname, "####",suffix)
|
||||
basename = fname
|
||||
|
||||
asmfilename = basename + '.s'
|
||||
#binfilename = basename + '.bin'
|
||||
binfilename = basename + '.prog'
|
||||
|
||||
if "-n" in compiler_options:
|
||||
# Assembling stdlib won't work
|
||||
do_assemble = False
|
||||
do_emulator = False
|
||||
|
||||
if do_compile:
|
||||
run_compiler(infilename, compiler_options)
|
||||
if do_assemble:
|
||||
run_assembler(asmfilename)
|
||||
if do_emulator:
|
||||
run_emulator(binfilename, sys.argv[2:])
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
17
pcomp/platfile+.pas
Normal file
17
pcomp/platfile+.pas
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
(* Copyright 2021-2024 Sebastian Lederer. See the file LICENSE.md for details *)
|
||||
procedure openTextFile(var f:TextFile; filename:string);
|
||||
begin
|
||||
{$I-}
|
||||
assign(f, filename);
|
||||
reset(f);
|
||||
|
||||
if IOResult <> 0 then
|
||||
errorExit2('cannot open file', filename);
|
||||
{$I+}
|
||||
end;
|
||||
|
||||
procedure overwriteTextFile(var f:TextFile; filename:string);
|
||||
begin
|
||||
assign(f, filename);
|
||||
rewrite(f);
|
||||
end;
|
||||
12
pcomp/platfile+tdr.pas
Normal file
12
pcomp/platfile+tdr.pas
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
(* Copyright 2021-2024 Sebastian Lederer. See the file LICENSE.md for details *)
|
||||
procedure openTextFile(var f:TextFile; filename:string);
|
||||
begin
|
||||
open(f, filename, ModeReadOnly);
|
||||
if IOResult(f) <> 0 then
|
||||
errorExit2('cannot open file', filename);
|
||||
end;
|
||||
|
||||
procedure overwriteTextFile(var f:TextFile; filename:string);
|
||||
begin
|
||||
open(f, filename, ModeOverwrite);
|
||||
end;
|
||||
2
pcomp/platfile-types+.pas
Normal file
2
pcomp/platfile-types+.pas
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
(* Copyright 2021-2024 Sebastian Lederer. See the file LICENSE.md for details *)
|
||||
type TextFile = text;
|
||||
2
pcomp/platfile-types+tdr.pas
Normal file
2
pcomp/platfile-types+tdr.pas
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
(* Copyright 2021-2024 Sebastian Lederer. See the file LICENSE.md for details *)
|
||||
type TextFile = file;
|
||||
53
pcomp/platform+.pas
Normal file
53
pcomp/platform+.pas
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
(* Copyright 2021-2024 Sebastian Lederer. See the file LICENSE.md for details *)
|
||||
procedure initPlatform;
|
||||
begin
|
||||
outputPrefix := '';
|
||||
includePrefix := '..\lib\';
|
||||
end;
|
||||
|
||||
procedure newString(var s:StringRef;len:integer);
|
||||
begin
|
||||
new(s);
|
||||
end;
|
||||
|
||||
procedure openFileWithDefault(var f:InputFileType; filename:string);
|
||||
begin
|
||||
{$I-}
|
||||
assign(f, filename);
|
||||
reset(f);
|
||||
|
||||
if IOResult <> 0 then
|
||||
begin
|
||||
assign(f, includePrefix + '/' + filename);
|
||||
reset(f);
|
||||
if IOResult <> 0 then
|
||||
errorExit2('cannot open file', filename);
|
||||
end;
|
||||
{$I+}
|
||||
end;
|
||||
|
||||
procedure overwriteFile(var f:OutputFileType; filename:string);
|
||||
begin
|
||||
assign(f, outputPrefix + filename);
|
||||
rewrite(f);
|
||||
end;
|
||||
|
||||
function isdigit(aChar:char):boolean;
|
||||
begin
|
||||
isdigit := (ord(aChar) >= ord('0')) and (ord(aChar) <= ord('9'));
|
||||
end;
|
||||
|
||||
procedure ExecEditor(var filename:string; lineno:integer; errormsg:string);
|
||||
begin
|
||||
halt;
|
||||
end;
|
||||
|
||||
procedure ExecAssembler(var filename:string; doRun:boolean; editOnError:boolean);
|
||||
begin
|
||||
halt;
|
||||
end;
|
||||
|
||||
procedure ExecProgram(var filename:string);
|
||||
begin
|
||||
halt;
|
||||
end;
|
||||
76
pcomp/platform+tdr.pas
Normal file
76
pcomp/platform+tdr.pas
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
(* Copyright 2021-2024 Sebastian Lederer. See the file LICENSE.md for details *)
|
||||
procedure initPlatform;
|
||||
begin
|
||||
outputPrefix := '';
|
||||
includePrefix := '#SYSTEM:';
|
||||
end;
|
||||
|
||||
procedure newString(var s:StringRef;len:integer);
|
||||
begin
|
||||
new(s,len);
|
||||
end;
|
||||
|
||||
procedure openFileWithDefault(var f:InputFileType; filename:string);
|
||||
begin
|
||||
open(f, filename, ModeReadOnly);
|
||||
if IOResult(f) <> 0 then
|
||||
begin
|
||||
open(f, includePrefix + filename, ModeReadOnly);
|
||||
if IOResult(f) <> 0 then
|
||||
errorExit2('cannot open file', filename);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure overwriteFile(var f:OutputFileType; filename:string);
|
||||
begin
|
||||
open(f, outputPrefix + filename, ModeOverwrite);
|
||||
end;
|
||||
|
||||
procedure printExecErr(filename:string; error:integer);
|
||||
begin
|
||||
writeln('PExec failed for ', filename, ': ', ErrorStr(error));
|
||||
end;
|
||||
|
||||
procedure ExecEditor(var filename:string; lineno:integer; errormsg:string);
|
||||
var args:PArgVec;
|
||||
error:integer;
|
||||
digits:string[12];
|
||||
begin
|
||||
str(lineno, digits);
|
||||
args[0] := '-l'; args[1] := digits;
|
||||
args[2] := '-E'; args[3] := errormsg;
|
||||
args[4] := filename;
|
||||
PExec('#SYSTEM:editor.prog', args, 5, error);
|
||||
printExecErr('#SYSTEM:editor.prog', error);
|
||||
end;
|
||||
|
||||
procedure ExecAssembler(var filename:string; doRun:boolean; editOnError:boolean);
|
||||
var args:PArgVec;
|
||||
argPos:integer;
|
||||
error:integer;
|
||||
begin
|
||||
if doRun then
|
||||
begin
|
||||
args[0] := '-R';
|
||||
argPos := 1;
|
||||
end
|
||||
else
|
||||
argPos := 0;
|
||||
if editOnError then
|
||||
begin
|
||||
args[argPos] := '-e';
|
||||
argPos := argPos + 1;
|
||||
end;
|
||||
args[argPos] := filename;
|
||||
PExec('#SYSTEM:sasm.prog', args, argPos + 1, error);
|
||||
printExecErr('#SYSTEM:editor.prog', error);
|
||||
end;
|
||||
|
||||
procedure ExecProgram(var filename:string);
|
||||
var args:PArgVec;
|
||||
error:integer;
|
||||
begin
|
||||
writeln('Running ', filename, '...');
|
||||
PExec(filename, args, 0, error);
|
||||
printExecErr(filename, error);
|
||||
end;
|
||||
7
pcomp/platform-types+.pas
Normal file
7
pcomp/platform-types+.pas
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
(* Copyright 2021-2024 Sebastian Lederer. See the file LICENSE.md for details *)
|
||||
type
|
||||
OutputFileType = text;
|
||||
InputFileType = file of char;
|
||||
SymFileType = text;
|
||||
|
||||
ExecAction = (Edit, Assemble, Run);
|
||||
6
pcomp/platform-types+tdr.pas
Normal file
6
pcomp/platform-types+tdr.pas
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
(* Copyright 2021-2024 Sebastian Lederer. See the file LICENSE.md for details *)
|
||||
OutputFileType = file;
|
||||
InputFileType = file;
|
||||
SymFileType = file;
|
||||
|
||||
ExecAction = (Edit, Assemble, Run);
|
||||
2650
pcomp/sasm.pas
Normal file
2650
pcomp/sasm.pas
Normal file
File diff suppressed because it is too large
Load diff
884
pcomp/sdis.pas
Normal file
884
pcomp/sdis.pas
Normal file
|
|
@ -0,0 +1,884 @@
|
|||
(* Copyright 2021-2024 Sebastian Lederer. See the file LICENSE.md for details *)
|
||||
program sdis;
|
||||
{$R+}
|
||||
{$MODE objfpc}
|
||||
type
|
||||
InputFileType = file of char;
|
||||
OutputFileType = text;
|
||||
|
||||
KeywordString = string[128];
|
||||
IdentString = string[80];
|
||||
|
||||
SymbolType = (ConstSymbol, LabelSymbol, SpecialSymbol);
|
||||
|
||||
|
||||
Symbol = record
|
||||
name:KeywordString;
|
||||
value:integer;
|
||||
typ:SymbolType;
|
||||
end;
|
||||
|
||||
HashEntry = record
|
||||
key:integer;
|
||||
data:IdentString;
|
||||
next:^HashEntry;
|
||||
end;
|
||||
|
||||
HashRef = ^HashEntry;
|
||||
HashBucket = ^HashEntry;
|
||||
HashTable = array [0..255] of HashBucket;
|
||||
|
||||
var infile:InputfileType;
|
||||
filename:string;
|
||||
pc:integer;
|
||||
symbolTable: HashTable;
|
||||
|
||||
procedure errorExit;
|
||||
begin
|
||||
close(infile);
|
||||
halt;
|
||||
end;
|
||||
|
||||
procedure errorExit2(message1, message2: string);
|
||||
begin
|
||||
writeln;
|
||||
writeln('Error: ', message1, ' ', message2);
|
||||
errorExit;
|
||||
end;
|
||||
|
||||
procedure openFile(var f:InputFileType;var filename:string);
|
||||
begin
|
||||
{$I-}
|
||||
assign(f, filename);
|
||||
reset(f);
|
||||
|
||||
if IOResult <> 0 then
|
||||
errorExit2('cannot open file ', filename);
|
||||
{$I+}
|
||||
end;
|
||||
|
||||
function readChar:char;
|
||||
var c:char;
|
||||
begin
|
||||
read(infile,c);
|
||||
readChar := c;
|
||||
end;
|
||||
|
||||
procedure readEol;
|
||||
var c:char;
|
||||
begin
|
||||
c := readChar;
|
||||
if c = #13 then
|
||||
begin
|
||||
c := readChar;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
function readBin(len:integer):integer;
|
||||
var i,v:integer;
|
||||
c:char;
|
||||
begin
|
||||
v := 0;
|
||||
for i := 1 to len do
|
||||
begin
|
||||
c := readChar;
|
||||
v := v shl 8;
|
||||
v := v or ord(c);
|
||||
end;
|
||||
readBin := v;
|
||||
end;
|
||||
|
||||
function readAsciiBin(len:integer):integer;
|
||||
var i:integer;
|
||||
w:integer;
|
||||
c:char;
|
||||
bits:integer;
|
||||
begin
|
||||
bits := len * 8;
|
||||
w := 0;
|
||||
for i := 1 to bits do
|
||||
begin
|
||||
w := w shl 1;
|
||||
c := readChar;
|
||||
if c = '1' then
|
||||
w := w or 1
|
||||
else
|
||||
if c = '0' then begin end;
|
||||
end;
|
||||
readAsciiBin := w;
|
||||
|
||||
(* read end of line *)
|
||||
if (pc and 3) = 2 then
|
||||
readEol;
|
||||
end;
|
||||
|
||||
function readBytes(len:integer):integer;
|
||||
var w:integer;
|
||||
c:char;
|
||||
i:integer;
|
||||
begin
|
||||
w := 0;
|
||||
for i := 1 to len do
|
||||
begin
|
||||
read(infile, c);
|
||||
w := (w shl 8) or ord(c);
|
||||
end;
|
||||
|
||||
readBytes := w;
|
||||
writeln('readBytes ',len, ': ', w);
|
||||
end;
|
||||
|
||||
function readInstruction:integer;
|
||||
begin
|
||||
(* readInstruction := readBytes(2); *)
|
||||
readInstruction := readBin(2);
|
||||
end;
|
||||
|
||||
function readWord:integer;
|
||||
begin
|
||||
readWord := readBin(4);
|
||||
end;
|
||||
|
||||
function convertHex(var digits:KeywordString):integer;
|
||||
var i,v,len:integer;
|
||||
c:char;
|
||||
begin
|
||||
len := length(digits);
|
||||
|
||||
i := 1;
|
||||
convertHex := 0;
|
||||
|
||||
while i <= len do
|
||||
begin
|
||||
convertHex := convertHex shl 4;
|
||||
c := UpCase(digits[i]);
|
||||
if (c >= 'A') and (c <= 'F') then
|
||||
v := ord(c) - ord('A') + 10
|
||||
else
|
||||
if (c >= '0') and (c <= '9') then
|
||||
v := ord(c) - ord('0')
|
||||
else
|
||||
errorExit2('invalid number',digits);
|
||||
convertHex := convertHex + v;
|
||||
i := i + 1;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure hexstr(value:integer;var output:string);
|
||||
var i:integer;
|
||||
nibble:integer;
|
||||
c:char;
|
||||
begin
|
||||
output := '00000000';
|
||||
|
||||
for i := 8 downto 1 do
|
||||
begin
|
||||
nibble := value and $F;
|
||||
if nibble > 9 then
|
||||
c := chr( ord('A') + nibble - 10)
|
||||
else
|
||||
c := chr( ord('0') + nibble);
|
||||
|
||||
output[i] := c;
|
||||
value := value shr 4;
|
||||
if value = 0 then break;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure writeHex(value:integer);
|
||||
var s:string;
|
||||
begin
|
||||
hexstr(value,s);
|
||||
write('$',s);
|
||||
end;
|
||||
|
||||
procedure printAsciiWord(w:integer);
|
||||
var i:integer;
|
||||
c:char;
|
||||
begin
|
||||
write('"');
|
||||
for i := 1 to 4 do
|
||||
begin
|
||||
c := chr(((w shr 24) and $FF));
|
||||
w := w shl 8;
|
||||
if (c < ' ') or (c > '~') then
|
||||
c := '.';
|
||||
write(c);
|
||||
end;
|
||||
write('"');
|
||||
end;
|
||||
|
||||
|
||||
{$R-}
|
||||
(* disable range checks for 32-bit hash functions *)
|
||||
|
||||
(* hash a 32-bit integer into an 8-bit integer *)
|
||||
function hashint(value:integer):integer;
|
||||
var i:integer;
|
||||
begin
|
||||
hashint := 0;
|
||||
value := value xor $B298AB49; (* some random 32-bit constant *)
|
||||
for i := 1 to 4 do
|
||||
begin
|
||||
hashint := hashint xor (value and $FF);
|
||||
value := value shr 8;
|
||||
end;
|
||||
end;
|
||||
|
||||
{$R+}
|
||||
|
||||
procedure putHashed(var t:HashTable;key:integer;var data:KeywordString);
|
||||
var i:integer;
|
||||
newEntry:^HashEntry;
|
||||
bucket:HashBucket;
|
||||
begin
|
||||
new(newEntry);
|
||||
newEntry^.data := data;
|
||||
newEntry^.key := key;
|
||||
|
||||
i := hashint(key);
|
||||
bucket := t[i];
|
||||
newEntry^.next := bucket;
|
||||
t[i] := newEntry;
|
||||
end;
|
||||
|
||||
function getHashed(var t:HashTable;key:integer):HashRef;
|
||||
var bucket:HashBucket;
|
||||
current:^HashEntry;
|
||||
found:boolean;
|
||||
begin
|
||||
getHashed := nil;
|
||||
bucket := t[hashint(key)];
|
||||
current := bucket;
|
||||
found := false;
|
||||
|
||||
while (current <> nil) and not found do
|
||||
begin
|
||||
if current^.key = key then
|
||||
begin
|
||||
getHashed := current;
|
||||
found := true;
|
||||
end;
|
||||
current := current^.next;
|
||||
end;
|
||||
end;
|
||||
|
||||
function getHashBucket(var t:HashTable;key:integer):HashRef;
|
||||
begin
|
||||
getHashBucket := t[hashint(key)];
|
||||
end;
|
||||
|
||||
procedure dumpHash(var t:HashTable);
|
||||
var i:integer;
|
||||
bucket:HashBucket;
|
||||
current:HashRef;
|
||||
begin
|
||||
for i := 0 to 255 do
|
||||
begin
|
||||
write('bucket ',i:4, ' ');
|
||||
bucket := t[i];
|
||||
current := bucket;
|
||||
while current <> nil do
|
||||
begin
|
||||
write(current^.key, ':', current^.data, ' ');
|
||||
current := current^.next;
|
||||
end;
|
||||
writeln;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure printEol;
|
||||
begin
|
||||
writeln;
|
||||
end;
|
||||
|
||||
procedure printHex(value:integer);
|
||||
var s:string[8];
|
||||
begin
|
||||
write('$');
|
||||
hexstr(value, s);
|
||||
write(s);
|
||||
end;
|
||||
|
||||
procedure printOperand(operand:integer);
|
||||
var sym:HashRef;
|
||||
begin
|
||||
sym := getHashed(symbolTable, operand);
|
||||
if sym <> nil then
|
||||
begin
|
||||
write(sym^.data);
|
||||
write(' ; ');
|
||||
end;
|
||||
|
||||
printHex(operand);
|
||||
end;
|
||||
|
||||
procedure printSpacedOperand(operand:integer);
|
||||
begin
|
||||
write(' ');
|
||||
printOperand(operand);
|
||||
end;
|
||||
|
||||
(* operates on numbers with less than 32 bits, signmask indicates the
|
||||
highest bit which is the sign *)
|
||||
function makepositive(operand, signmask:integer):integer;
|
||||
begin
|
||||
if (operand and signmask) <> 0 then
|
||||
makepositive := signmask - (operand and (not signmask))
|
||||
else
|
||||
makepositive := operand;
|
||||
end;
|
||||
|
||||
function signExtend(operand, signmask:integer):integer;
|
||||
begin
|
||||
if (operand and signmask) <> 0 then
|
||||
signExtend := -(signmask - (operand and (not signmask)))
|
||||
else
|
||||
signExtend := operand;
|
||||
end;
|
||||
|
||||
procedure printSignedOperand(operand, signmask:integer);
|
||||
var sym:HashRef;
|
||||
begin
|
||||
write(' ');
|
||||
sym := getHashed(symbolTable, operand);
|
||||
if sym <> nil then
|
||||
write(sym^.data)
|
||||
else
|
||||
begin
|
||||
if operand and signmask <> 0 then
|
||||
begin
|
||||
write('-');
|
||||
operand := makepositive(operand, signmask);
|
||||
end;
|
||||
printHex(operand);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure decodeBranch(operand:integer);
|
||||
begin
|
||||
write('BRANCH');
|
||||
printSpacedOperand(pc + signExtend(operand, $1000));
|
||||
end;
|
||||
|
||||
procedure decodeCbranch(operand:integer);
|
||||
begin
|
||||
write('CBRANCH');
|
||||
if (operand and 1) = 1 then
|
||||
write('.NZ')
|
||||
else
|
||||
write('.Z');
|
||||
|
||||
printSpacedOperand(pc + signExtend((operand and $FFFE), $100));
|
||||
end;
|
||||
|
||||
|
||||
procedure decodeLoadc(operand:integer);
|
||||
begin
|
||||
write('LOADC');
|
||||
printSignedOperand(operand, $1000);
|
||||
end;
|
||||
|
||||
procedure decodeLoadStore(name:string; operand:integer);
|
||||
begin
|
||||
write(name);
|
||||
if (operand and 1) = 1 then
|
||||
write('.B');
|
||||
|
||||
printSpacedOperand(operand and $FFFE);
|
||||
end;
|
||||
|
||||
procedure decodeModifier(name:string;value, mask:integer; operand:integer; visible:boolean);
|
||||
begin
|
||||
if (operand and mask) = value then
|
||||
if visible then
|
||||
write('.',name);
|
||||
end;
|
||||
|
||||
procedure decodeXfer(operand:integer);
|
||||
begin
|
||||
write('XFER');
|
||||
decodeModifier('RSM1', $0300, $0300, operand, true);
|
||||
decodeModifier('RS0', $0000, $0300, operand, false);
|
||||
decodeModifier('RS1', $0100, $0300, operand, true);
|
||||
decodeModifier('R2P', $0080, $0080, operand, true);
|
||||
decodeModifier('P2R', $0040, $0040, operand, true);
|
||||
decodeModifier('SM1', $0030, $0030, operand, true);
|
||||
decodeModifier('S0', $0000, $0030, operand, false);
|
||||
decodeModifier('S1', $0010, $0030, operand, true);
|
||||
decodeModifier('X2P', $0001, $0001, operand, true);
|
||||
end;
|
||||
|
||||
procedure printCmpOperand(operand:integer);
|
||||
begin
|
||||
case operand of
|
||||
2: write('EQ');
|
||||
6: write('NE');
|
||||
1: write('LT');
|
||||
3: write('LE');
|
||||
5: write('GE');
|
||||
7: write('GT');
|
||||
else write('<unknown:',operand,'>');
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure decodeAlu(operand:integer);
|
||||
var aluop:integer;
|
||||
begin
|
||||
write('ALU');
|
||||
decodeModifier('ADD', $0000, $1e00, operand, true);
|
||||
decodeModifier('SUB', $0200, $1e00, operand, true);
|
||||
decodeModifier('NOT', $0400, $1e00, operand, true);
|
||||
decodeModifier('AND', $0600, $1e00, operand, true);
|
||||
decodeModifier('OR', $0800, $1e00, operand, true);
|
||||
decodeModifier('XOR', $0a00, $1e00, operand, true);
|
||||
decodeModifier('CMP', $0c00, $1e00, operand, true);
|
||||
decodeModifier('Y', $0e00, $1e00, operand, true);
|
||||
decodeModifier('SHR', $1000, $1e00, operand, true);
|
||||
decodeModifier('SHL', $1200, $1e00, operand, true);
|
||||
decodeModifier('INC', $1400, $1e00, operand, true);
|
||||
decodeModifier('DEC', $1600, $1e00, operand, true);
|
||||
decodeModifier('BPLC', $1a00, $1e00, operand, true);
|
||||
decodeModifier('BROT', $1c00, $1e00, operand, true);
|
||||
decodeModifier('BSEL', $1e00, $1e00, operand, true);
|
||||
decodeModifier('CMPU', $1800, $1e00, operand, true);
|
||||
|
||||
decodeModifier('SM1', $0030, $0030, operand, true);
|
||||
decodeModifier('S0', $0000, $0030, operand, false);
|
||||
decodeModifier('S1', $0010, $0030, operand, true);
|
||||
decodeModifier('X2Y', $0040, $0040, operand, true);
|
||||
decodeModifier('NX2Y', $0000, $0040, operand, false);
|
||||
decodeModifier('XT', $0080, $0080, operand, true);
|
||||
|
||||
aluop := operand and $1e00;
|
||||
operand := operand and 15;
|
||||
|
||||
if (aluop = $1800) or (aluop = $0c00) then
|
||||
begin
|
||||
write(' ');
|
||||
printCmpOperand(operand);
|
||||
end
|
||||
else
|
||||
if operand > 0 then
|
||||
printSpacedOperand(operand);
|
||||
end;
|
||||
|
||||
procedure decodeLoadrel(offset:integer);
|
||||
begin
|
||||
write('LOADREL');
|
||||
printSpacedOperand(pc + offset);
|
||||
end;
|
||||
|
||||
procedure decodeMem(operand:integer);
|
||||
begin
|
||||
if (operand and $0200) <> 0 then
|
||||
begin
|
||||
write('STOREI');
|
||||
decodeModifier('SM1', $0030, $0030, operand, false);
|
||||
decodeModifier('S0', $0000, $0030, operand, true);
|
||||
decodeModifier('S1', $0010, $0030, operand, true);
|
||||
decodeModifier('X2Y', $0040, $0040, operand, true);
|
||||
decodeModifier('NX2Y', $0000, $0040, operand, false);
|
||||
decodeModifier('XT', $0080, $0080, operand, true);
|
||||
end
|
||||
else
|
||||
begin
|
||||
write('LOADI');
|
||||
decodeModifier('SM1', $0030, $0030, operand, true);
|
||||
decodeModifier('S0', $0000, $0030, operand, false);
|
||||
decodeModifier('S1', $0010, $0030, operand, true);
|
||||
decodeModifier('X2Y', $0040, $0040, operand, true);
|
||||
decodeModifier('NX2Y', $0000, $0040, operand, false);
|
||||
decodeModifier('XT', $0080, $0080, operand, true);
|
||||
end;
|
||||
operand := operand and 15;
|
||||
if operand > 0 then
|
||||
printSpacedOperand(operand);
|
||||
end;
|
||||
|
||||
procedure printRegOperand(operand:integer);
|
||||
begin
|
||||
case operand of
|
||||
0: write('FP');
|
||||
1: write('BP');
|
||||
2: write('RP');
|
||||
3: write('IV');
|
||||
4: write('IR');
|
||||
5: write('ESP');
|
||||
else write('<unknown:',operand,'>');
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure decodeReg(operand:integer);
|
||||
begin
|
||||
if (operand and $0200) <> 0 then
|
||||
write('STOREREG ')
|
||||
else
|
||||
write('LOADREG ');
|
||||
|
||||
operand := operand and 15;
|
||||
printRegOperand(operand);
|
||||
end;
|
||||
|
||||
procedure decodeExt(operand:integer);
|
||||
var extop:integer;
|
||||
begin
|
||||
extop := (operand and $1C00) shr 10;
|
||||
|
||||
if extop = 0 then
|
||||
decodeReg(operand)
|
||||
else
|
||||
if extop = 1 then
|
||||
decodeMem(operand)
|
||||
(*
|
||||
else
|
||||
if extop = 2 then
|
||||
begin
|
||||
{ unused }
|
||||
end *)
|
||||
else
|
||||
if extop = 3 then
|
||||
begin
|
||||
write('FPADJ ');
|
||||
printSignedOperand(operand and $03FF, $200);
|
||||
end
|
||||
else
|
||||
if extop = 5 then
|
||||
decodeLoadrel(operand and $03FF)
|
||||
else
|
||||
write('<EXT unknown:', extop,'>');
|
||||
end;
|
||||
|
||||
procedure decodeInstruction(w:integer);
|
||||
var baseIns:integer;
|
||||
baseOperand:integer;
|
||||
begin
|
||||
baseIns := (w and $E000) shr 13;
|
||||
baseOperand := (w and $1FFF);
|
||||
|
||||
(* writeln(baseIns, ' ', baseOperand); *)
|
||||
|
||||
if baseIns = 0 then
|
||||
decodeBranch(baseOperand)
|
||||
else
|
||||
if baseIns = 1 then
|
||||
decodeAlu(baseOperand)
|
||||
else
|
||||
if baseIns = 2 then
|
||||
decodeLoadStore('STORE', baseOperand)
|
||||
else
|
||||
if baseIns = 3 then
|
||||
decodeXfer(baseOperand)
|
||||
else
|
||||
if baseIns = 4 then
|
||||
decodeLoadStore('LOAD', baseOperand)
|
||||
else
|
||||
if baseIns = 5 then
|
||||
decodeCbranch(baseOperand)
|
||||
else
|
||||
if baseIns = 6 then
|
||||
decodeLoadc(baseOperand)
|
||||
else
|
||||
if baseIns = 7 then
|
||||
decodeExt(baseOperand)
|
||||
else
|
||||
write('???');
|
||||
|
||||
pc := pc + 2;
|
||||
|
||||
(* write(' (', baseIns, ')');
|
||||
writeHex(w); *)
|
||||
|
||||
printEol;
|
||||
end;
|
||||
|
||||
function isConstantPool(sym:HashRef):boolean;
|
||||
begin
|
||||
isConstantPool := false;
|
||||
|
||||
if sym <> nil then
|
||||
begin
|
||||
if length(sym^.data) >= 4 then
|
||||
isConstantPool :=
|
||||
(sym^.data[1] = '_') and
|
||||
(sym^.data[2] = 'C') and
|
||||
(sym^.data[3] = 'P') and
|
||||
(sym^.data[4] = '_');
|
||||
end;
|
||||
end;
|
||||
|
||||
function isStringConstant(sym:HashRef):boolean;
|
||||
begin
|
||||
isStringConstant := false;
|
||||
|
||||
if sym <> nil then
|
||||
begin
|
||||
if length(sym^.data) >= 5 then
|
||||
isStringConstant :=
|
||||
(sym^.data[1] = '_') and
|
||||
(sym^.data[2] = 'C') and
|
||||
(sym^.data[3] = '_') and
|
||||
(sym^.data[4] = 'S') and
|
||||
(sym^.data[5] = '_');
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure decodeIntConstant(upperHalf:integer);
|
||||
var lowerHalf:integer;
|
||||
w:integer;
|
||||
begin
|
||||
{$R-}
|
||||
pc := pc + 2;
|
||||
(* need to increment pc in two steps
|
||||
because readBin uses the pc to detect line endings
|
||||
(which is probably a bad idea *)
|
||||
|
||||
lowerHalf := readInstruction;
|
||||
w := (upperHalf shl 16) or lowerHalf;
|
||||
|
||||
pc := pc + 2;
|
||||
|
||||
write('.WORD ');
|
||||
printOperand(w);
|
||||
writeln;
|
||||
|
||||
{$R+}
|
||||
end;
|
||||
|
||||
procedure printPaddedLabel(sym:HashRef); forward;
|
||||
procedure printPc; forward;
|
||||
|
||||
procedure printLeadin;
|
||||
begin
|
||||
printPc;
|
||||
printPaddedLabel(nil);
|
||||
end;
|
||||
|
||||
procedure decodeString(upperHalf:integer);
|
||||
var lowerHalf:integer;
|
||||
curLength,maxLength:integer;
|
||||
i,wordCount:integer;
|
||||
w:integer;
|
||||
begin
|
||||
pc := pc + 2;
|
||||
lowerHalf := readInstruction;
|
||||
pc := pc + 2;
|
||||
curLength := (upperHalf shl 16) or lowerHalf;
|
||||
maxLength := readWord;
|
||||
|
||||
write('.WORD ');
|
||||
printHex(curLength);
|
||||
writeln;
|
||||
|
||||
printLeadin;
|
||||
write('.WORD ');
|
||||
printHex(maxLength);
|
||||
writeln;
|
||||
pc := pc + 4;
|
||||
|
||||
wordCount := curLength;
|
||||
if maxLength > curLength then
|
||||
wordCount := maxLength;
|
||||
|
||||
wordCount := (wordCount + 3) shr 2;
|
||||
|
||||
for i := 1 to wordCount do
|
||||
begin
|
||||
w := readWord;
|
||||
printLeadin;
|
||||
write('.BYTE ');
|
||||
printAsciiWord(w);
|
||||
writeln;
|
||||
pc := pc + 4;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure printPaddedLabel(sym:HashRef);
|
||||
var pad:integer;
|
||||
begin
|
||||
pad := 24;
|
||||
if sym <> nil then
|
||||
begin
|
||||
write(sym^.data);
|
||||
write(':');
|
||||
|
||||
pad := pad - length(sym^.data) - 1;
|
||||
end;
|
||||
|
||||
while pad > 0 do
|
||||
begin
|
||||
write(' ');
|
||||
pad := pad - 1;
|
||||
end;
|
||||
write(' ');
|
||||
end;
|
||||
|
||||
procedure printPc;
|
||||
var hexaddr:string[8];
|
||||
begin
|
||||
hexstr(pc, hexaddr);
|
||||
write(hexaddr, ' ');
|
||||
end;
|
||||
|
||||
procedure printLabels(adr:integer);
|
||||
var bucket:HashBucket;
|
||||
current:HashRef;
|
||||
first:boolean;
|
||||
begin
|
||||
(* there can be multiple labels
|
||||
at an instruction address,
|
||||
so go through all elements
|
||||
in the corresponding hash bucket *)
|
||||
first := true;
|
||||
bucket := getHashBucket(symbolTable, adr);
|
||||
current := bucket;
|
||||
while current <> nil do
|
||||
begin
|
||||
if current^.key = adr then
|
||||
begin
|
||||
if not first then
|
||||
begin
|
||||
writeln;
|
||||
(* printPc; *)
|
||||
write(' ');
|
||||
end
|
||||
else
|
||||
first := false;
|
||||
|
||||
printPaddedLabel(current);
|
||||
end;
|
||||
current := current^.next;
|
||||
end;
|
||||
|
||||
if first then
|
||||
printPaddedLabel(nil);
|
||||
end;
|
||||
|
||||
procedure decodeFile;
|
||||
var w:integer;
|
||||
sym:HashRef;
|
||||
begin
|
||||
while not eof(infile) do
|
||||
begin
|
||||
printPc;
|
||||
printLabels(pc);
|
||||
|
||||
w := readInstruction;
|
||||
|
||||
sym := getHashed(symbolTable, pc);
|
||||
if isConstantPool(sym) then
|
||||
decodeIntConstant(w)
|
||||
else
|
||||
if isStringConstant(sym) then
|
||||
decodeString(w)
|
||||
else
|
||||
decodeInstruction(w);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure testHash;
|
||||
var s:string;
|
||||
result:HashRef;
|
||||
i:integer;
|
||||
begin
|
||||
s := 'einszweidrei';
|
||||
putHashed(symbolTable, 123, s);
|
||||
s := 'vierfuenf';
|
||||
putHashed(symbolTable, 45, s);
|
||||
s := 'null';
|
||||
putHashed(symbolTable, 0, s);
|
||||
s := '0x7FFF1234';
|
||||
putHashed(symbolTable, $7FFF1234, s);
|
||||
|
||||
result := getHashed(symbolTable, 123);
|
||||
writeln('getHashed 123:', result^.data);
|
||||
|
||||
result := getHashed(symbolTable, 45);
|
||||
writeln('getHashed 45:', result^.data);
|
||||
|
||||
result := getHashed(symbolTable, 0);
|
||||
writeln('getHashed 0:', result^.data);
|
||||
|
||||
result := getHashed(symbolTable, $7FFF1234);
|
||||
writeln('getHashed $7FFF1234:', result^.data);
|
||||
|
||||
for i := 1 to 5000 do
|
||||
begin
|
||||
str(i,s);
|
||||
putHashed(symbolTable,i,s);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure readKeyword(var fil:InputFileType; var wordBuf:string);
|
||||
var c:char;
|
||||
skipWhite:boolean;
|
||||
done:boolean;
|
||||
begin
|
||||
wordBuf := '';
|
||||
done := false;
|
||||
skipWhite := true;
|
||||
|
||||
repeat
|
||||
read(fil,c);
|
||||
if c in [ ' ', #9, #13, #10, #0 ] then
|
||||
begin
|
||||
if not skipWhite then
|
||||
done := true;
|
||||
end
|
||||
else
|
||||
begin
|
||||
wordBuf := wordBuf + c;
|
||||
skipWhite := false;
|
||||
end;
|
||||
until done or eof(fil);
|
||||
if c = #13 then (* skip over CR/LF *)
|
||||
read(fil,c);
|
||||
end;
|
||||
|
||||
procedure readSymbolTable(var filename:string);
|
||||
var buf:string;
|
||||
fil:InputFileType;
|
||||
symStr:string;
|
||||
num:integer;
|
||||
begin
|
||||
openFile(fil, filename);
|
||||
while not eof(fil) do
|
||||
begin
|
||||
readKeyword(fil,buf);
|
||||
readKeyword(fil,symStr);
|
||||
num := convertHex(buf);
|
||||
putHashed(symbolTable, num, symStr);
|
||||
end;
|
||||
|
||||
close(fil);
|
||||
end;
|
||||
|
||||
function parseOrigin(s:string):integer;
|
||||
var i,c:integer;
|
||||
begin
|
||||
val(s,i,c);
|
||||
if c > 0 then
|
||||
errorExit2('invalid number',s);
|
||||
parseOrigin := i;
|
||||
end;
|
||||
|
||||
begin
|
||||
if paramCount < 1 then halt;
|
||||
|
||||
if paramCount >= 2 then
|
||||
begin
|
||||
filename := paramStr(2);
|
||||
readSymbolTable(filename);
|
||||
end;
|
||||
|
||||
if paramCount >= 3 then
|
||||
pc := parseOrigin(paramStr(3));
|
||||
|
||||
filename := paramStr(1);
|
||||
openFile(infile, filename);
|
||||
decodeFile;
|
||||
close(infile);
|
||||
|
||||
(* dumpHash(symbolTable); *)
|
||||
end.
|
||||
289
pcomp/treeimpl.pas
Normal file
289
pcomp/treeimpl.pas
Normal file
|
|
@ -0,0 +1,289 @@
|
|||
(* Copyright 2021-2024 Sebastian Lederer. See the file LICENSE.md for details *)
|
||||
function makeTreeNode(var d:TreeData;var key:string;nparent:TreeRef):TreeRef;
|
||||
var newNode:TreeRef;
|
||||
newKey:^string;
|
||||
begin
|
||||
new(newNode);
|
||||
{ new(newKey,length(key)); }
|
||||
newString(newKey, length(key));
|
||||
new(newNode^.data);
|
||||
newKey^ := key;
|
||||
with newNode^ do
|
||||
begin
|
||||
key := newKey;
|
||||
parent := nparent;
|
||||
left := nil;
|
||||
right := nil;
|
||||
height := 1;
|
||||
data^ := d;
|
||||
end;
|
||||
makeTreeNode := newNode;
|
||||
end;
|
||||
|
||||
function MeasureTree(root:TreeRef):integer;
|
||||
var leftHeight, rightHeight:integer;
|
||||
begin
|
||||
if root = nil then
|
||||
MeasureTree := 0
|
||||
else
|
||||
begin
|
||||
if root^.left <> nil then
|
||||
leftHeight := root^.left^.height
|
||||
else
|
||||
leftHeight := 0;
|
||||
if root^.right <> nil then
|
||||
rightHeight := root^.right^.height
|
||||
else
|
||||
rightHeight := 0;
|
||||
if rightHeight > leftHeight then
|
||||
MeasureTree := rightHeight + 1
|
||||
else
|
||||
MeasureTree := leftHeight + 1;
|
||||
end;
|
||||
end;
|
||||
|
||||
function GetTreeBalance(root:TreeRef):integer;
|
||||
begin
|
||||
if root = nil then
|
||||
GetTreeBalance := 0
|
||||
else
|
||||
GetTreeBalance := MeasureTree(root^.left) - MeasureTree(root^.right);
|
||||
end;
|
||||
|
||||
function RotateTreeRight(x:TreeRef):TreeRef;
|
||||
var z,tmp:TreeRef;
|
||||
begin
|
||||
(* writeln('RotateTreeRight at ', x^.key^); *)
|
||||
z := x^.left;
|
||||
tmp := z^.right;
|
||||
z^.right := x;
|
||||
z^.parent := x^.parent;
|
||||
x^.parent := z;
|
||||
x^.left := tmp;
|
||||
if tmp <> nil then
|
||||
tmp^.parent := x;
|
||||
x^.height := MeasureTree(x);
|
||||
z^.height := MeasureTree(z);
|
||||
RotateTreeRight := z;
|
||||
end;
|
||||
|
||||
function RotateTreeLeft(x:TreeRef):TreeRef;
|
||||
var z,tmp:TreeRef;
|
||||
begin
|
||||
(* writeln('RotateTreeLeft at ', x^.key^); *)
|
||||
z := x^.right;
|
||||
tmp := z^.left;
|
||||
z^.left := x;
|
||||
z^.parent := x^.parent;
|
||||
x^.parent := z;
|
||||
x^.right := tmp;
|
||||
if tmp <> nil then
|
||||
tmp^.parent := x;
|
||||
x^.height := MeasureTree(x);
|
||||
z^.height := MeasureTree(z);
|
||||
RotateTreeLeft := z;
|
||||
end;
|
||||
|
||||
function TreeInsert4(root:TreeRef;var key:string;var data:TreeData;
|
||||
parent:TreeRef):TreeRef;
|
||||
var balance:integer;
|
||||
begin
|
||||
if root = nil then
|
||||
root := makeTreeNode(data, key, parent)
|
||||
else
|
||||
if key < root^.key^ then
|
||||
root^.left := TreeInsert4(root^.left, key, data, root)
|
||||
else
|
||||
root^.right := TreeInsert4(root^.right, key, data, root);
|
||||
|
||||
root^.height := MeasureTree(root);
|
||||
|
||||
balance := GetTreeBalance(root);
|
||||
if balance > 1 then
|
||||
begin
|
||||
if key < root^.left^.key^ then
|
||||
root := RotateTreeRight(root)
|
||||
else
|
||||
begin
|
||||
root^.left := RotateTreeLeft(root^.left);
|
||||
root := RotateTreeRight(root);
|
||||
end;
|
||||
end
|
||||
else
|
||||
if balance < -1 then
|
||||
begin
|
||||
if key > root^.right^.key^ then
|
||||
root := RotateTreeLeft(root)
|
||||
else
|
||||
begin
|
||||
root^.right := RotateTreeRight(root^.right);
|
||||
root := RotateTreeLeft(root);
|
||||
end;
|
||||
end;
|
||||
|
||||
TreeInsert4 := root;
|
||||
end;
|
||||
|
||||
procedure TreeInsert(var root:TreeRef;var key:string;var data:TreeData);
|
||||
begin
|
||||
root := TreeInsert4(root,key,data,nil);
|
||||
end;
|
||||
|
||||
procedure DisposeTreeNode(node:TreeRef);
|
||||
begin
|
||||
dispose(node^.key);
|
||||
dispose(node^.data);
|
||||
dispose(node);
|
||||
end;
|
||||
|
||||
function TreeLeftmost(node:TreeRef):TreeRef;
|
||||
begin
|
||||
TreeLeftmost := nil;
|
||||
if node <> nil then
|
||||
begin
|
||||
repeat
|
||||
TreeLeftmost := node;
|
||||
node := node^.left;
|
||||
until node = nil;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TreeDeleteFn(root:TreeRef;var key:string):TreeRef;
|
||||
var tmp,oldParent:TreeRef;
|
||||
balance:integer;
|
||||
begin
|
||||
if root <> nil then
|
||||
begin
|
||||
if key < root^.key^ then
|
||||
root^.left := TreeDeleteFn(root^.left, key)
|
||||
else
|
||||
if key > root^.key^ then
|
||||
root^.right := TreeDeleteFn(root^.right, key)
|
||||
else
|
||||
begin
|
||||
if root^.left = nil then
|
||||
begin
|
||||
tmp := root;
|
||||
oldParent := root^.parent;
|
||||
root := root^.right;
|
||||
if root <> nil then
|
||||
root^.parent := oldParent;
|
||||
DisposeTreeNode(tmp);
|
||||
end
|
||||
else
|
||||
if root^.right = nil then
|
||||
begin
|
||||
tmp := root;
|
||||
oldParent := root^.parent;
|
||||
root := root^.left;
|
||||
if root <> nil then
|
||||
root^.parent := oldParent;
|
||||
DisposeTreeNode(tmp);
|
||||
end
|
||||
else
|
||||
begin
|
||||
tmp := TreeLeftmost(root^.right);
|
||||
root^.key^ := tmp^.key^;
|
||||
root^.data^ := tmp^.data^;
|
||||
oldParent := tmp^.parent;
|
||||
if oldParent^.left = tmp then
|
||||
oldParent^.left := TreeDeleteFn(oldParent^.left, tmp^.key^)
|
||||
else
|
||||
if oldParent^.right = tmp then
|
||||
oldParent^.right := TreeDeleteFn(oldParent^.right, tmp^.key^)
|
||||
else
|
||||
begin
|
||||
writeln('TreeDelete internal error at', root^.key^);
|
||||
end;
|
||||
end;
|
||||
|
||||
if root <> nil then
|
||||
begin
|
||||
root^.height := MeasureTree(root);
|
||||
balance := GetTreeBalance(root);
|
||||
if balance > 1 then
|
||||
begin
|
||||
if GetTreeBalance(root^.left) >=0 then
|
||||
root := RotateTreeRight(root)
|
||||
else
|
||||
begin
|
||||
root^.left := RotateTreeLeft(root^.left);
|
||||
root := RotateTreeRight(root);
|
||||
end;
|
||||
end
|
||||
else
|
||||
if balance < -1 then
|
||||
begin
|
||||
if GetTreeBalance(root^.right) <= 0 then
|
||||
root := RotateTreeLeft(root)
|
||||
else
|
||||
begin
|
||||
root^.right := RotateTreeRight(root^.right);
|
||||
root := RotateTreeLeft(root);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
TreeDeleteFn := root;
|
||||
end;
|
||||
|
||||
procedure TreeDelete(var root:TreeRef;var key:string);
|
||||
begin
|
||||
root := TreeDeleteFn(root,key);
|
||||
end;
|
||||
|
||||
function TreeSearch(root:TreeRef;var key:string):TreeDataRef;
|
||||
begin
|
||||
if root <> nil then
|
||||
begin
|
||||
if key = root^.key^ then
|
||||
TreeSearch := root^.data
|
||||
else
|
||||
if key < root^.key^ then
|
||||
TreeSearch := TreeSearch(root^.left, key)
|
||||
else
|
||||
TreeSearch := TreeSearch(root^.right, key);
|
||||
end
|
||||
else
|
||||
TreeSearch := nil;
|
||||
end;
|
||||
|
||||
procedure TreeWalkStart(t:TreeRef; var state:TreeWalkState);
|
||||
begin
|
||||
(* start at leftmost node of the tree *)
|
||||
state.currentNode := TreeLeftmost(t);
|
||||
end;
|
||||
|
||||
procedure TreeWalkNext(var state:TreeWalkState;var res:TreeRef);
|
||||
var last,current,right:TreeRef;
|
||||
begin
|
||||
current := state.currentNode;
|
||||
|
||||
res := current;
|
||||
|
||||
if current <> nil then
|
||||
begin
|
||||
(* descending right *)
|
||||
if current^.right <> nil then
|
||||
begin
|
||||
state.currentNode := TreeLeftmost(current^.right);
|
||||
end
|
||||
else (* ascending *)
|
||||
begin
|
||||
repeat
|
||||
last := current;
|
||||
current := current^.parent;
|
||||
if current <> nil then
|
||||
right := current^.right;
|
||||
until (right <> last) or (current = nil); (* ascend left edges *)
|
||||
state.currentNode := current;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TreeWalkFirst(t:TreeRef; var state:TreeWalkState; var first:TreeRef);
|
||||
begin
|
||||
TreeWalkStart(t, state);
|
||||
TreeWalkNext(state, first);
|
||||
end;
|
||||
26
pcomp/treetypes.pas
Normal file
26
pcomp/treetypes.pas
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
(* Copyright 2021-2024 Sebastian Lederer. See the file LICENSE.md for details *)
|
||||
{
|
||||
type TreedataType = (TDString, TDInteger);
|
||||
|
||||
type Treedata = record
|
||||
case typ:Treedatatype of
|
||||
TDString:(stringdata:string);
|
||||
TDInteger:(intdata:integer);
|
||||
end;
|
||||
}
|
||||
type StringRef = ^string;
|
||||
|
||||
type TreeNode = record
|
||||
parent: ^TreeNode;
|
||||
left,right: ^TreeNode;
|
||||
height: integer;
|
||||
key: StringRef;
|
||||
data: ^Treedata;
|
||||
end;
|
||||
|
||||
type TreeRef = ^TreeNode;
|
||||
TreeDataRef = ^Treedata;
|
||||
|
||||
type TreeWalkState = record
|
||||
currentNode:TreeRef;
|
||||
end;
|
||||
52
progs/dumpdir.pas
Normal file
52
progs/dumpdir.pas
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
(* Copyright 2021-2024 Sebastian Lederer. See the file LICENSE.md for details *)
|
||||
program dumpdir;
|
||||
var volname:string;
|
||||
volid:integer;
|
||||
|
||||
(* we use some stuff internal to stdlib.pas *)
|
||||
procedure getdirslot(volumeid:integer;slotNo:integer;var result:DirectorySlot;var error:integer);
|
||||
external;
|
||||
|
||||
procedure dumpdir(volid:integer);
|
||||
var dirs:DirectorySlot;
|
||||
i:integer;
|
||||
lastSlot:integer;
|
||||
error:integer;
|
||||
begin
|
||||
lastSlot := volumeTable[volid].part.dirSize - 1;
|
||||
openvolumeid(volid);
|
||||
|
||||
for i := 0 to lastSlot do
|
||||
begin
|
||||
getdirslot(volid, i, dirs, error);
|
||||
with dirs do
|
||||
begin
|
||||
write('slot ', i, ' ', name, ' ', sizeBytes, ' G', generation);
|
||||
if SlotFirst in flags then write(' First');
|
||||
if SlotExtent in flags then write(' Extent');
|
||||
if SlotReserved in flags then write(' Resvd');
|
||||
if SlotDeleted in flags then write(' Del');
|
||||
if SlotFree in flags then write(' Free');
|
||||
if SlotEndScan in flags then write(' End');
|
||||
writeln;
|
||||
if SlotEndScan in flags then break;
|
||||
end;
|
||||
end;
|
||||
|
||||
closevolumeid(volid);
|
||||
end;
|
||||
|
||||
begin
|
||||
if ParamCount > 0 then
|
||||
volname := ParamStr(1)
|
||||
else
|
||||
begin
|
||||
write('Volume name> ');
|
||||
readln(volname);
|
||||
end;
|
||||
volid := findvolume(volname);
|
||||
if volid < 1 then
|
||||
writeln('Volume not found.')
|
||||
else
|
||||
dumpdir(volid);
|
||||
end.
|
||||
2491
progs/editor.pas
Normal file
2491
progs/editor.pas
Normal file
File diff suppressed because it is too large
Load diff
742
progs/partmgr.pas
Normal file
742
progs/partmgr.pas
Normal file
|
|
@ -0,0 +1,742 @@
|
|||
(* Copyright 2021-2024 Sebastian Lederer. See the file LICENSE.md for details *)
|
||||
program partmgr;
|
||||
const MaxPartitions = 32;
|
||||
LastPartBlock = 7;
|
||||
PartsPerBlock = 8;
|
||||
|
||||
var partTable:array[0..LastPartBlock] of PartitionTableBlock;
|
||||
changed:array[0..LastPartBlock] of boolean;
|
||||
detectedCardSize:integer;
|
||||
cmd:char;
|
||||
done:boolean;
|
||||
lastPartNo:integer;
|
||||
|
||||
function flags2str(flags:PartFlags):string;
|
||||
begin
|
||||
flags2str := '';
|
||||
|
||||
if PartEnabled in flags then flags2str := flags2str + 'E ';
|
||||
if PartBoot in flags then flags2str := flags2str + 'B ';
|
||||
if PartLast in flags then flags2str := flags2str + 'L ';
|
||||
if PartPhysical in flags then flags2str := flags2str + 'P ';
|
||||
if PartDefault in flags then flags2str := flags2str + 'D ';
|
||||
end;
|
||||
|
||||
function str2flags(var s:string):PartFlags;
|
||||
begin
|
||||
str2flags := [];
|
||||
|
||||
if 'E' in s then str2flags := str2flags + [PartEnabled];
|
||||
if 'B' in s then str2flags := str2flags + [PartBoot];
|
||||
if 'L' in s then str2flags := str2flags + [PartLast];
|
||||
if 'P' in s then str2flags := str2flags + [PartPhysical];
|
||||
if 'D' in s then str2flags := str2flags + [PartDefault];
|
||||
end;
|
||||
|
||||
function sanitizeName(var name:string):string;
|
||||
begin
|
||||
if (length(name) <= 32) and (maxlength(name) = 32) then
|
||||
sanitizeName := name
|
||||
else
|
||||
sanitizeName := '<invalid>';
|
||||
end;
|
||||
|
||||
procedure changeNumber(prompt:string; var num:integer);
|
||||
var buf:string;
|
||||
err:integer;
|
||||
digits:string;
|
||||
begin
|
||||
str(num, digits);
|
||||
buf := prompt + ' [' + digits + ']> ';
|
||||
write(buf:30);
|
||||
readln(buf);
|
||||
val(buf,num,err);
|
||||
end;
|
||||
|
||||
|
||||
procedure readPartTable;
|
||||
var done:boolean;
|
||||
curblk:integer;
|
||||
error:integer;
|
||||
devid:integer;
|
||||
begin
|
||||
done := false;
|
||||
curblk := 0;
|
||||
devid := 0; (* we only support one device *)
|
||||
|
||||
while not done do
|
||||
begin
|
||||
changed[curBlk] := false;
|
||||
|
||||
readPartBlk(curblk, partTable[curblk], error, devid);
|
||||
if error <> 0 then
|
||||
begin
|
||||
done := true;
|
||||
writeln('Error ', error,
|
||||
' reading partition block ', curblk);
|
||||
end
|
||||
else
|
||||
curblk := curblk + 1;
|
||||
|
||||
|
||||
if curBlk > LastPartBlock then
|
||||
done := true;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure writePartBlock(no:integer);
|
||||
var error:integer;
|
||||
devid:integer;
|
||||
begin
|
||||
devid := 0;
|
||||
writePartBlk(no, partTable[no], error, devid);
|
||||
if error <> 0 then
|
||||
writeln('Error ', error,
|
||||
' reading partition block ', no);
|
||||
end;
|
||||
|
||||
procedure writePartitions;
|
||||
var blkNo:integer;
|
||||
begin
|
||||
for blkNo := 0 to LastPartBlock do
|
||||
begin
|
||||
if changed[blkNo] then
|
||||
begin
|
||||
writeln('Writing back partition block ',blkNo);
|
||||
writePartBlock(blkNo);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function getPartition(partNo:integer):Partition;
|
||||
var blkNo:integer;
|
||||
begin
|
||||
blkNo := partNo div PartsPerBlock;
|
||||
if (blkNo < 0) or (blkNo > LastPartBlock) then
|
||||
writeln('internal error: invalid part no in getPartition')
|
||||
else
|
||||
getPartition := partTable[blkNo][partNo mod PartsPerBlock];
|
||||
{ writeln('** getPartition: ', blkNo, ' ', partNo mod PartsPerBlock); }
|
||||
end;
|
||||
|
||||
procedure putPartition(var part:Partition; partNo:integer);
|
||||
var blkNo:integer;
|
||||
begin
|
||||
blkNo := partNo div PartsPerBlock;
|
||||
{ writeln('** putPartition: ', blkNo, ' ', partNo mod PartsPerBlock); }
|
||||
if (blkNo < 0) or (blkNo > LastPartBlock) then
|
||||
writeln('internal error: invalid part no in getPartition')
|
||||
else
|
||||
begin
|
||||
partTable[blkNo][partNo mod PartsPerBlock] := part;
|
||||
changed[blkNo] := true;
|
||||
end;
|
||||
end;
|
||||
|
||||
function isEmptyPart(var part:Partition):boolean;
|
||||
begin
|
||||
isEmptyPart := (part.startBlock = 0) and (part.blocks = 0);
|
||||
end;
|
||||
|
||||
procedure printPartTable;
|
||||
var blkNo, partNo:integer;
|
||||
partBlk:PartitionTableBlock;
|
||||
part:Partition;
|
||||
totalPartNo:integer;
|
||||
begin
|
||||
totalPartNo := 0;
|
||||
|
||||
writeln('Partition Table:');
|
||||
writeln('No. ', 'Flags':11, 'Name':32, 'Start':10, 'Size':10);
|
||||
|
||||
for blkNo := 0 to LastPartBlock do
|
||||
begin
|
||||
partBlk := partTable[blkNo];
|
||||
for partNo := 0 to PartsPerBlock - 1 do
|
||||
begin
|
||||
part := partBlk[partNo];
|
||||
|
||||
if not isEmptyPart(part) then
|
||||
begin
|
||||
write(totalPartNo:3, ': ', flags2Str(part.flags):11,
|
||||
sanitizeName(part.name):32,
|
||||
part.startBlock:10,
|
||||
part.blocks:10);
|
||||
if PartBoot in part.flags then write(' ', part.bootBlocks);
|
||||
writeln;
|
||||
lastPartNo := totalPartNo;
|
||||
end;
|
||||
totalPartNo := totalPartNo + 1;
|
||||
end;
|
||||
end;
|
||||
writeln('Flags: P=Physical B=Boot E=Enabled L=Last D=Default');
|
||||
end;
|
||||
|
||||
function askPartNo:integer;
|
||||
var i:integer;
|
||||
s:string;
|
||||
begin
|
||||
askPartNo := -1;
|
||||
write('Enter partition number (0-', lastPartNo, ')> ');
|
||||
readln(s);
|
||||
if length(s) > 0 then
|
||||
begin
|
||||
val(s,askPartNo,i);
|
||||
if i > 0 then
|
||||
writeln('Invalid partition number');
|
||||
end;
|
||||
end;
|
||||
|
||||
function askConfirmPartNo:integer;
|
||||
var partNo:integer;
|
||||
part:Partition;
|
||||
answer:char;
|
||||
begin
|
||||
askConfirmPartNo := -1;
|
||||
|
||||
partNo := askPartNo;
|
||||
|
||||
if partNo >= 0 then
|
||||
begin
|
||||
part := getPartition(partNo);
|
||||
write('Any data on partition ', partNo,
|
||||
' (', sanitizeName(part.name), ') ',
|
||||
'will be destroyed. Sure [y/n]? ');
|
||||
readln(answer);
|
||||
if upcase(answer) = 'Y' then
|
||||
askConfirmPartNo := partNo;
|
||||
end;
|
||||
end;
|
||||
|
||||
function guessExtentSize(blocks:integer):integer;
|
||||
begin
|
||||
if blocks >= 4194304 then (* 2 GB *)
|
||||
guessExtentSize := 1048576 (* use 1MB extents *)
|
||||
else
|
||||
if blocks >= 1048576 then (* 512 MB *)
|
||||
guessExtentSize := 524288 (* use 512K extents *)
|
||||
else
|
||||
if blocks >= 524288 then (* 256 MB *)
|
||||
guessExtentSize := 131072
|
||||
else
|
||||
if blocks >= 262144 then (* 128 MB *)
|
||||
guessExtentSize := 65536
|
||||
else
|
||||
if blocks >= 32768 then (* 16 MB *)
|
||||
guessExtentSize := 16384
|
||||
else
|
||||
guessExtentSize := 8192;
|
||||
end;
|
||||
|
||||
function getDirSize(extentSize,blocks:integer):integer;
|
||||
begin
|
||||
getDirSize := blocks div (extentSize div 512);
|
||||
end;
|
||||
|
||||
procedure createFilesystem(partNo:integer); forward;
|
||||
|
||||
procedure addVolume;
|
||||
var nextFreeBlock:integer;
|
||||
nextBlock:integer;
|
||||
extentSize:integer;
|
||||
size:integer;
|
||||
i:integer;
|
||||
part:Partition;
|
||||
maxBlocks:integer;
|
||||
freeBlocks:integer;
|
||||
newPartNo:integer;
|
||||
newPart:Partition;
|
||||
begin
|
||||
part := getPartition(0);
|
||||
maxBlocks := part.blocks;
|
||||
nextFreeBlock := 0;
|
||||
(* read all partitions *)
|
||||
for i := 1 to lastPartNo do
|
||||
begin
|
||||
part := getPartition(i);
|
||||
nextBlock := part.startBlock + part.blocks;
|
||||
if nextBlock > nextFreeBlock then
|
||||
nextFreeBlock := nextBlock;
|
||||
end;
|
||||
(* remember last used block *)
|
||||
writeln('next free partition: ', lastPartNo + 1,
|
||||
' next free block: ', nextFreeBlock);
|
||||
|
||||
freeBlocks := maxBlocks - nextFreeBlock;
|
||||
|
||||
if freeBlocks < 1 then
|
||||
writeln('Cannot add partition - no free blocks after last partition.')
|
||||
else
|
||||
begin
|
||||
newPartNo := lastPartNo + 1;
|
||||
(* remove last partition flag on previous last partition *)
|
||||
part.flags := part.flags - [PartLast];
|
||||
putPartition(part, lastPartNo);
|
||||
|
||||
(* create new partition *)
|
||||
size := freeBlocks;
|
||||
changeNumber('Size (blocks)', size);
|
||||
write('Name> ':30);
|
||||
readln(newPart.name);
|
||||
|
||||
newPart.startBlock := nextFreeBlock;
|
||||
newPart.blocks := size;
|
||||
newPart.extentSize := guessExtentSize(size);
|
||||
newPart.dirSize := getDirSize(newPart.extentSize, size);
|
||||
newPart.bootBlocks := 0;
|
||||
(* mark new partition as last partition *)
|
||||
newPart.flags := [ PartEnabled, PartLast ];
|
||||
putPartition(newPart, newPartNo);
|
||||
|
||||
writeln('Partition ', newPartNo, ' created, extent size:', newPart.extentSize,
|
||||
' directory size: ', newPart.dirSize);
|
||||
createFilesystem(newPartNo);
|
||||
|
||||
lastPartNo := lastPartNo + 1;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure renameVolume;
|
||||
var partNo:integer;
|
||||
newName:string;
|
||||
part:Partition;
|
||||
|
||||
begin
|
||||
partNo := askPartNo;
|
||||
if partNo >= 0 then
|
||||
begin
|
||||
part := getPartition(partNo);
|
||||
writeln('Old partition/volume name: ', sanitizeName(part.name));
|
||||
write('New partion/volume name: ');
|
||||
readln(newName);
|
||||
if length(newName) > 0 then
|
||||
begin
|
||||
part.name := newName;
|
||||
putPartition(part, partNo);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure toggleDefaultFlag;
|
||||
var partNo:integer;
|
||||
part:Partition;
|
||||
|
||||
begin
|
||||
partNo := askPartNo;
|
||||
if partNo >= 0 then
|
||||
begin
|
||||
part := getPartition(partNo);
|
||||
write('Default flag ');
|
||||
if PartDefault in part.flags then
|
||||
begin
|
||||
part.flags := part.flags - [PartDefault];
|
||||
write('cleared');
|
||||
end
|
||||
else
|
||||
begin
|
||||
part.flags := part.flags + [PartDefault];
|
||||
write('set');
|
||||
end;
|
||||
writeln(' on partition ', partNo, ' (', sanitizeName(part.name), ').');
|
||||
putPartition(part, partNo);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure deleteVolume;
|
||||
var partNo:integer;
|
||||
part:Partition;
|
||||
begin
|
||||
partNo := askConfirmPartNo;
|
||||
if partNo >= 0 then
|
||||
begin
|
||||
part := getPartition(partNo);
|
||||
part.flags := [];
|
||||
part.name := '';
|
||||
part.startBlock := 0;
|
||||
part.blocks := 0;
|
||||
part.extentSize := 0;
|
||||
part.dirSize := 0;
|
||||
part.bootBlocks := 0;
|
||||
putPartition(part, partNo);
|
||||
|
||||
writeln('Partition ', partNo, ' deleted.');
|
||||
|
||||
(* try to fix last partition flag *)
|
||||
(* only works if the previous entry has
|
||||
a valid partition *)
|
||||
if partNo = lastPartNo then
|
||||
begin
|
||||
lastPartNo := lastPartNo - 1;
|
||||
part := getPartition(lastPartNo);
|
||||
part.flags := part.flags + [PartLast];
|
||||
putPartition(part, lastPartNo);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure validatePartTable;
|
||||
var partNo:integer;
|
||||
phys:Partition;
|
||||
part,part2:Partition;
|
||||
answer:char;
|
||||
p,p2:integer;
|
||||
valid:boolean;
|
||||
begin
|
||||
valid := true;
|
||||
|
||||
phys := getPartition(0);
|
||||
if not (PartPhysical in phys.flags) then
|
||||
begin
|
||||
writeln('PHYS partition missing, initialize card first!');
|
||||
exit;
|
||||
end;
|
||||
|
||||
if phys.blocks <> detectedCardSize then
|
||||
begin
|
||||
write('PHYS partition size does not match detected card size, fix? [y/n]');
|
||||
readln(answer);
|
||||
if upcase(answer) = 'Y' then
|
||||
begin
|
||||
phys.blocks := detectedCardSize;
|
||||
putPartition(phys,0);
|
||||
end
|
||||
else
|
||||
valid := false;
|
||||
end;
|
||||
|
||||
for p := 1 to lastPartNo do
|
||||
begin
|
||||
part := getPartition(p);
|
||||
if (part.startBlock < 0) or (part.startBlock + part.blocks > phys.blocks) then
|
||||
begin
|
||||
writeln('Partition ', p, ' outside of physical bounds.');
|
||||
valid := false;
|
||||
end;
|
||||
|
||||
if PartEnabled in part.flags then
|
||||
if part.dirSize <> getDirSize(part.extentSize, part.blocks) then
|
||||
begin
|
||||
write('Partition ', p, ' has an invalid directory size (is ');
|
||||
writeln(part.dirSize, ', should be ',
|
||||
getDirSize(part.extentSize, part.blocks), ').');
|
||||
valid := false;
|
||||
end;
|
||||
|
||||
for p2 := 1 to lastPartNo do
|
||||
begin
|
||||
part2 := getPartition(p2);
|
||||
if (p <> p2) then
|
||||
begin
|
||||
if ((part.startBlock >= part2.startBlock) and
|
||||
(part.startBlock < part2.startBlock + part2.blocks)) or
|
||||
((part2.startBlock > part.startBlock) and
|
||||
(part2.startBlock < part.startBlock + part.blocks))
|
||||
then
|
||||
begin
|
||||
writeln('Partition ',p ,' overlaps with partition ', p2);
|
||||
valid := false;
|
||||
end;
|
||||
|
||||
if (part.name = part2.name) and (p > p2) then
|
||||
begin
|
||||
writeln('Duplicate volume name ', part.name);
|
||||
valid := false;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
write('Partition table is ');
|
||||
if not valid then write('in');
|
||||
writeln('valid.');
|
||||
end;
|
||||
|
||||
procedure checkNewCard; forward;
|
||||
|
||||
procedure initializeCard;
|
||||
var part:Partition;
|
||||
answer:char;
|
||||
p:integer;
|
||||
begin
|
||||
writeln('Initializing a card will create an empty partition table with');
|
||||
writeln('the standard PHYS and BOOT partitions.');
|
||||
write('This will likely destroy any data on the card - sure? [y/n] ');
|
||||
readln(answer);
|
||||
if upcase(answer) <> 'Y' then exit;
|
||||
|
||||
(* create PHYS partition using detectedcardblocks *)
|
||||
part.name := 'PHYS';
|
||||
part.startBlock := 0;
|
||||
part.blocks := detectedCardSize;
|
||||
part.flags := [PartPhysical];
|
||||
part.extentSize := 0;
|
||||
part.dirSize := 0;
|
||||
part.bootBlocks := 0;
|
||||
putPartition(part,0);
|
||||
|
||||
(* create BOOT partition without PartBoot flag *)
|
||||
part.name := 'BOOT';
|
||||
part.startBlock := 16; (* 16 possible partition blocks with 8 partitions each *)
|
||||
part.blocks := 8192 - 16; (* align first volume to 4MB *)
|
||||
part.flags := [PartBoot, PartLast];
|
||||
putPartition(part,1);
|
||||
|
||||
part.name := '';
|
||||
part.startBlock := 0;
|
||||
part.blocks := 0;
|
||||
part.flags := [];
|
||||
|
||||
for p := 2 to 7 do
|
||||
putPartition(part,p);
|
||||
|
||||
writeln('Empty partition table created.');
|
||||
end;
|
||||
|
||||
procedure createFilesystem(partNo:integer);
|
||||
var firstDirBlock:integer;
|
||||
slot:DirectorySlot;
|
||||
dirblk:DirBlock;
|
||||
dirblockCount:integer;
|
||||
metaSlotsCount:integer;
|
||||
dirSlotsPerBlock:integer;
|
||||
dirSlotsPerExtent:integer;
|
||||
part:Partition;
|
||||
ts:Timestamp;
|
||||
i,b:integer;
|
||||
error,devid:integer;
|
||||
begin
|
||||
devid := 0;
|
||||
ts := 0;
|
||||
|
||||
part := getPartition(partNo);
|
||||
firstDirBlock := part.startBlock;
|
||||
dirSlotsPerBlock := 512 div 64;
|
||||
dirSlotsPerExtent := part.extentSize div 64;
|
||||
dirblockCount := (part.dirSize - 1) div dirSlotsPerBlock + 1;
|
||||
metaSlotsCount := (part.dirSize - 1) div dirSlotsPerExtent + 1;
|
||||
|
||||
writeln('partition size: ', part.blocks);
|
||||
writeln('extent size: ', part.extentSize);
|
||||
writeln('directory size: ', part.dirSize);
|
||||
{ writeln('dirslots per extent:', dirSlotsPerExtent);
|
||||
writeln('dirblocks: ', dirblockCount);
|
||||
writeln('metaslots: ', metaSlotsCount);
|
||||
writeln('first dir block: ', firstDirBlock);
|
||||
}
|
||||
for b := firstDirBlock to firstDirBlock + dirblockCount - 1 do
|
||||
begin
|
||||
for i := 0 to dirSlotsPerBlock - 1 do
|
||||
begin
|
||||
if metaSlotsCount > 0 then
|
||||
begin
|
||||
(* write DIR/Reserved directory slots *)
|
||||
slot.name := 'DIR';
|
||||
slot.flags := [ SlotReserved ];
|
||||
metaSlotsCount := metaSlotsCount - 1;
|
||||
end
|
||||
else
|
||||
begin
|
||||
(* write Free + EndScan directory slots *)
|
||||
slot.name := '';
|
||||
slot.flags := [ SlotFree , SlotEndScan ];
|
||||
end;
|
||||
slot.sizeBytes := 0;
|
||||
slot.createTime := ts;
|
||||
slot.modTime := ts;
|
||||
slot.generation := 0;
|
||||
slot.owner := 0;
|
||||
|
||||
dirBlk[i] := slot;
|
||||
end;
|
||||
writedirblk(b, dirBlk, error, devid);
|
||||
if error > 0 then
|
||||
writeln('error writing block ', b, ': ', error);
|
||||
end;
|
||||
writeln('Volume ', part.name, ' initialized.');
|
||||
end;
|
||||
|
||||
procedure initializeVolume;
|
||||
var partNo:integer;
|
||||
part:Partition;
|
||||
begin
|
||||
partNo := askConfirmPartNo;
|
||||
if partNo >= 0 then
|
||||
begin
|
||||
part := getPartition(partNo);
|
||||
if not (PartEnabled in part.flags) then
|
||||
writeln('Wrong partition flags (must be Enabled)')
|
||||
else
|
||||
createFilesystem(partNo);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure rawEdit;
|
||||
var partNo:integer;
|
||||
newName:string;
|
||||
part:Partition;
|
||||
buf:string;
|
||||
begin
|
||||
writeln('Raw editing partition entry - use caution!');
|
||||
|
||||
partNo := askPartNo;
|
||||
if partNo >= 0 then
|
||||
begin
|
||||
part := getPartition(partNo);
|
||||
writeln('Volume name: ', sanitizeName(part.name));
|
||||
write('Flags> ':30);
|
||||
readln(buf);
|
||||
if length(buf) > 0 then
|
||||
part.flags := str2flags(buf);
|
||||
changeNumber('Start block', part.startBlock);
|
||||
changeNumber('Size (blocks)', part.blocks);
|
||||
changeNumber('Extent size (blocks)', part.extentSize);
|
||||
changeNumber('Dir size (slots)', part.dirSize);
|
||||
changeNumber('Boot blocks', part.bootBlocks);
|
||||
|
||||
putPartition(part, partNo);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure installBoot;
|
||||
var bootfile:file;
|
||||
name:string;
|
||||
part:Partition;
|
||||
partNo:integer;
|
||||
buf:IOBlock;
|
||||
b,blkCount:integer;
|
||||
devId:integer;
|
||||
error:integer;
|
||||
|
||||
procedure readWordsIntoBuf;
|
||||
var i:integer;
|
||||
w:integer;
|
||||
c1,c2,c3,c4:char;
|
||||
begin
|
||||
for i := 0 to 127 do
|
||||
begin
|
||||
if not eof(bootfile) then
|
||||
begin
|
||||
read(bootfile, c1, c2, c3, c4);
|
||||
w := (ord(c1) shl 24) or
|
||||
(ord(c2) shl 16) or
|
||||
(ord(c3) shl 8) or
|
||||
ord(c4);
|
||||
end
|
||||
else
|
||||
w := 0;
|
||||
buf[i] := w;
|
||||
end;
|
||||
end;
|
||||
|
||||
begin
|
||||
devId := 0; (* only one device supported *)
|
||||
partNo := 1; (* BOOT partition is always at position 1 *)
|
||||
|
||||
part := getPartition(partNo);
|
||||
if part.name <> 'BOOT' then
|
||||
begin
|
||||
writeln('No BOOT partition at position 1.');
|
||||
exit;
|
||||
end;
|
||||
|
||||
write('Boot file name> ');
|
||||
readln(name);
|
||||
if length(name) > 0 then
|
||||
begin
|
||||
open(bootfile, name, ModeReadonly);
|
||||
if IOResult(bootfile) <> 0 then
|
||||
writeln('Error opening file: ', ErrorStr(IOResult(bootfile)))
|
||||
else
|
||||
begin
|
||||
blkCount := filesize(bootfile) div 512 + 1;
|
||||
if blkCount > part.blocks then
|
||||
writeln('Boot partition too small, need ', blkCount)
|
||||
else
|
||||
begin
|
||||
part.bootBlocks := blkCount;
|
||||
if not (PartBoot in part.flags) then
|
||||
begin
|
||||
write('Boot flag set');
|
||||
writeln(' on partition ', partNo,
|
||||
' (', sanitizeName(part.name), ').');
|
||||
part.flags := part.flags + [PartBoot];
|
||||
end;
|
||||
putPartition(part, partNo);
|
||||
for b := 0 to blkCount - 1 do
|
||||
begin
|
||||
readWordsIntoBuf;
|
||||
writeblock(part.startBlock + b, buf, error, devId);
|
||||
if error <> 0 then
|
||||
writeln('Error in writeblock ', b, ': ', error);
|
||||
end;
|
||||
writeln(blkCount, ' boot blocks written.');
|
||||
end;
|
||||
close(bootfile);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure showMenu;
|
||||
begin
|
||||
writeln;
|
||||
writeln('L)ist partitions V)alidate partition table T)oggle default volume flag');
|
||||
writeln('A)dd volume R)ename volume D)elete volume I)nitialize volume');
|
||||
writeln('Read N)ew card Initialize C)ard');
|
||||
writeln('E)dit partition Install B)oot file eX)it');
|
||||
write('> ');
|
||||
end;
|
||||
|
||||
procedure invalidCommand;
|
||||
begin
|
||||
writeln('Invalid command.');
|
||||
end;
|
||||
|
||||
procedure command(cmd:char; var done:boolean);
|
||||
begin
|
||||
case cmd of
|
||||
'L': printPartTable;
|
||||
'A': addVolume;
|
||||
'R': renameVolume;
|
||||
'D': deleteVolume;
|
||||
'V': validatePartTable;
|
||||
'T': toggleDefaultFlag;
|
||||
'I': initializeVolume;
|
||||
'N': checkNewCard;
|
||||
'C': initializeCard;
|
||||
'B': installBoot;
|
||||
'E': rawEdit;
|
||||
'X',#24: done := true;
|
||||
else invalidCommand;
|
||||
end;
|
||||
end;
|
||||
|
||||
function changesPending:boolean;
|
||||
var i:integer;
|
||||
begin
|
||||
changesPending := false;
|
||||
for i := 0 to LastPartBlock do
|
||||
if changed[i] then changesPending := true;
|
||||
end;
|
||||
|
||||
procedure checkNewCard;
|
||||
begin
|
||||
if changesPending then
|
||||
writeln('WARNING: Discarding partition table changes.');
|
||||
initDevices;
|
||||
detectedCardSize := cardsize;
|
||||
writeln('Detected card size: ', detectedCardSize);
|
||||
readPartTable;
|
||||
printPartTable;
|
||||
end;
|
||||
|
||||
begin
|
||||
checkNewCard;
|
||||
|
||||
repeat
|
||||
showMenu;
|
||||
read(cmd);
|
||||
writeln;
|
||||
command(Upcase(cmd), done);
|
||||
until done;
|
||||
writePartitions;
|
||||
end.
|
||||
221
progs/reclaim.pas
Normal file
221
progs/reclaim.pas
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
(* Copyright 2021-2024 Sebastian Lederer. See the file LICENSE.md for details *)
|
||||
program reclaim;
|
||||
var volname:string;
|
||||
ch:char;
|
||||
count:integer;
|
||||
|
||||
(* we use some stuff internal to stdlib.pas *)
|
||||
procedure getdirslot(volumeid:integer;slotNo:integer;var result:DirectorySlot;var error:integer);
|
||||
external;
|
||||
procedure putdirslot(volumeid:integer;slotNo:integer;var dirslot:DirectorySlot;var error:integer);
|
||||
external;
|
||||
|
||||
procedure scanVolume(volname:string;dryrun:boolean;verbose:boolean;var reclaimCount:integer);
|
||||
var volid:integer;
|
||||
i:integer;
|
||||
error:integer;
|
||||
dirslot:DirectorySlot;
|
||||
done:boolean;
|
||||
fileCount, deletedCount:integer;
|
||||
freeCount:integer;
|
||||
fileSlotCount:integer;
|
||||
reservedCount:integer;
|
||||
freeAreaCount:integer;
|
||||
inFreeArea:boolean;
|
||||
endSlot:integer;
|
||||
lastUsed:integer;
|
||||
deletedExtent:boolean;
|
||||
|
||||
procedure clearDirSlot;
|
||||
begin
|
||||
reclaimCount := reclaimCount + 1;
|
||||
|
||||
if not dryrun then
|
||||
begin
|
||||
dirslot.name := '';
|
||||
dirslot.flags := [SlotFree];
|
||||
dirslot.sizeBytes := 0;
|
||||
dirslot.createTime := 0;
|
||||
dirslot.modTime := 0;
|
||||
dirslot.generation := 0;
|
||||
|
||||
putdirslot(volid, i, dirslot, error);
|
||||
if error <> IONoError then
|
||||
begin
|
||||
write('Error writing directory slot ',i);
|
||||
writeln(': ', ErrorStr(error));
|
||||
done := true;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure markLastSlot;
|
||||
var slotNo:integer;
|
||||
begin
|
||||
(* we actually mark the slot after the last used slot *)
|
||||
if not dryrun then
|
||||
begin
|
||||
if lastUsed < endSlot then
|
||||
begin
|
||||
writeln('Updating directory...');
|
||||
slotNo := lastUsed + 1;
|
||||
getdirslot(volid, slotNo, dirslot, error);
|
||||
if error <> IONoError then
|
||||
begin
|
||||
write('Error reading directory slot ', slotNo);
|
||||
writeln(': ', ErrorStr(error));
|
||||
end;
|
||||
|
||||
if not (SlotEndScan in dirslot.flags) then
|
||||
dirslot.flags := dirslot.flags + [SlotEndScan];
|
||||
|
||||
putdirslot(volid, slotNo, dirslot, error);
|
||||
if error <> IONoError then
|
||||
begin
|
||||
write('Error writing directory slot ', lastUsed);
|
||||
writeln(': ', ErrorStr(error));
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure beginFreeArea;
|
||||
begin
|
||||
freeCount := freeCount + 1;
|
||||
if not inFreeArea then
|
||||
begin
|
||||
inFreeArea := true;
|
||||
freeAreaCount := freeAreaCount + 1;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure endFreeArea;
|
||||
begin
|
||||
if inFreeArea then
|
||||
inFreeArea := false;
|
||||
end;
|
||||
|
||||
begin
|
||||
volid := findvolume(volname);
|
||||
if volid < 1 then
|
||||
writeln('Volume ', volname, ' not found.')
|
||||
else
|
||||
begin
|
||||
done := false;
|
||||
deletedExtent := false;
|
||||
inFreeArea := false;
|
||||
fileCount := 0;
|
||||
deletedCount := 0;
|
||||
reclaimCount := 0;
|
||||
freeCount := 0;
|
||||
reservedCount := 0;
|
||||
fileSlotCount := 0;
|
||||
freeAreaCount := 0;
|
||||
lastUsed := 0;
|
||||
|
||||
openvolumeid(volid);
|
||||
i := volumeTable[volid].startSlot;
|
||||
endSlot := volumeTable[volid].part.dirSize - 1;
|
||||
|
||||
if verbose then
|
||||
begin
|
||||
write('Volume ', volname);
|
||||
write(' start slot:', i);
|
||||
write(' dir size: ', endSlot + 1);
|
||||
writeln(' extent size: ', volumeTable[volid].part.extentSize);
|
||||
end;
|
||||
|
||||
writeln('Reading directory...');
|
||||
repeat
|
||||
getdirslot(volid, i, dirslot, error);
|
||||
if error <> IONoError then
|
||||
begin
|
||||
write('Error reading directory slot ',i);
|
||||
writeln(': ', ErrorStr(error));
|
||||
done := true;
|
||||
end
|
||||
else
|
||||
begin
|
||||
if SlotEndScan in dirslot.flags then
|
||||
done := true;
|
||||
if SlotFirst in dirslot.flags then
|
||||
begin
|
||||
lastUsed := i;
|
||||
fileCount := fileCount + 1;
|
||||
deletedExtent := false;
|
||||
endFreeArea;
|
||||
end
|
||||
else
|
||||
if SlotDeleted in dirslot.flags then
|
||||
begin
|
||||
deletedCount := deletedCount + 1;
|
||||
deletedExtent := true;
|
||||
clearDirSlot;
|
||||
(* we consider a deleted file
|
||||
as a free area here *)
|
||||
if not dryrun then
|
||||
beginFreeArea;
|
||||
end
|
||||
else
|
||||
if SlotExtent in dirslot.flags then
|
||||
begin
|
||||
if deletedExtent then
|
||||
clearDirSlot
|
||||
else
|
||||
lastUsed := i;
|
||||
end
|
||||
else
|
||||
if SlotReserved in dirslot.flags then
|
||||
reservedCount := reservedCount + 1
|
||||
else
|
||||
if SlotFree in dirslot.flags then
|
||||
beginFreeArea;
|
||||
end;
|
||||
if i = endSlot then
|
||||
done := true;
|
||||
i := i + 1;
|
||||
until done;
|
||||
|
||||
markLastSlot;
|
||||
closevolumeid(volid);
|
||||
i := i - 1;
|
||||
|
||||
if verbose then
|
||||
begin
|
||||
writeln('last used slot: ', lastUsed);
|
||||
writeln('max slots: ', endSlot + 1);
|
||||
writeln('free slots: ', endSlot - i + freeCount);
|
||||
writeln('reserved slots: ', reservedCount);
|
||||
writeln;
|
||||
end;
|
||||
|
||||
write(fileCount, ' files, ', deletedCount, ' deleted files, ');
|
||||
write(reclaimCount);
|
||||
if dryrun then
|
||||
writeln(' reclaimable slots, ', freeAreaCount, ' free regions.')
|
||||
else
|
||||
writeln(' reclaimed slots, ', freeAreaCount, ' free regions.');
|
||||
end;
|
||||
end;
|
||||
|
||||
begin
|
||||
if ParamCount > 0 then
|
||||
volname := ParamStr(1)
|
||||
else
|
||||
begin
|
||||
write('Volume name> ');
|
||||
readln(volname);
|
||||
end;
|
||||
|
||||
initDevices;
|
||||
scanVolume(volname, true, true, count);
|
||||
|
||||
if count > 0 then
|
||||
begin
|
||||
write('Proceed with reclaim (y/n)? ');
|
||||
read(ch);
|
||||
writeln;
|
||||
if upcase(ch) = 'Y' then
|
||||
scanVolume(volname, false, false, count);
|
||||
end;
|
||||
end.
|
||||
481
progs/shell.pas
Normal file
481
progs/shell.pas
Normal file
|
|
@ -0,0 +1,481 @@
|
|||
(* Copyright 2021-2024 Sebastian Lederer. See the file LICENSE.md for details *)
|
||||
program shell;
|
||||
const EDITORPROG = '#SYSTEM:editor.prog';
|
||||
COMPILERPROG = '#SYSTEM:pcomp.prog';
|
||||
ASMPROG = '#SYSTEM:sasm.prog';
|
||||
RECLAIMPROG = '#SYSTEM:reclaim.prog';
|
||||
|
||||
const PageMargin = 3;
|
||||
MenuHeight = 6;
|
||||
|
||||
var cmd:char;
|
||||
ShellWorkfile:pathnamestr external;
|
||||
ShellCmd:string[40] external;
|
||||
ShellArg:integer external;
|
||||
|
||||
procedure checkClock;
|
||||
var line:string;
|
||||
digits:string[4];
|
||||
error:integer;
|
||||
yy,mm,dd,h,m,s:integer;
|
||||
isValid:boolean;
|
||||
|
||||
function lineIsValid:boolean;
|
||||
var c:char;
|
||||
begin
|
||||
lineIsValid := false;
|
||||
|
||||
if length(line) = 14 then
|
||||
begin
|
||||
for c in line do
|
||||
if not isDigit(c) then
|
||||
break;
|
||||
lineIsValid := true;
|
||||
end;
|
||||
end;
|
||||
|
||||
function isInRange(v,lo,hi:integer):boolean;
|
||||
begin
|
||||
isInRange := (v>=lo) and (v<=hi);
|
||||
end;
|
||||
|
||||
begin
|
||||
if SysClock.year = 0 then
|
||||
begin
|
||||
writeln('System clock not set - please enter date and time:');
|
||||
repeat
|
||||
isValid := false;
|
||||
write('YYYYMMDDHHMMSS> ');
|
||||
readln(line);
|
||||
|
||||
if lineIsValid then
|
||||
begin
|
||||
isValid := true;
|
||||
digits := copy(line,1,4);
|
||||
val(digits, yy, error);
|
||||
isValid := isValid and isInRange(yy, 1950, 3000);
|
||||
|
||||
digits := copy(line,5,2);
|
||||
val(digits, mm, error);
|
||||
isValid := isValid and isInRange(mm, 1, 12);
|
||||
|
||||
digits := copy(line,7,2);
|
||||
val(digits, dd, error);
|
||||
isValid := isValid and isInRange(dd, 1, 31);
|
||||
|
||||
digits := copy(line,9,2);
|
||||
val(digits, h, error);
|
||||
isValid := isValid and isInRange(h, 0, 23);
|
||||
|
||||
digits := copy(line,11,2);
|
||||
val(digits, m, error);
|
||||
isValid := isValid and isInRange(m, 0, 59);
|
||||
|
||||
digits := copy(line,13,2);
|
||||
val(digits, s, error);
|
||||
isValid := isValid and isInRange(s, 0, 59);
|
||||
end;
|
||||
until isValid;
|
||||
|
||||
SysClock.year := yy;
|
||||
SysClock.month := mm;
|
||||
SysClock.day := dd;
|
||||
SysClock.hours := h;
|
||||
SysClock.minutes := m;
|
||||
SysClock.seconds := s;
|
||||
|
||||
writeln('System clock is ', DateStr(SysClock), ' ', TimeStr(SysClock, true));
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure writew(s:string;width:integer);
|
||||
var w,i:integer;
|
||||
begin
|
||||
write(s);
|
||||
w := width - length(s);
|
||||
if w > 0 then
|
||||
for i := 1 to w do
|
||||
write(' ');
|
||||
end;
|
||||
|
||||
procedure splitFilename(var n:filenamestr;
|
||||
var basename:filenamestr;var extension:filenamestr);
|
||||
var i:integer;
|
||||
begin
|
||||
for i := length(n) downto 1 do
|
||||
begin
|
||||
if n[i] = '.' then
|
||||
break;
|
||||
end;
|
||||
if i > 1 then
|
||||
begin
|
||||
basename := copy(n, 1, i - 1);
|
||||
extension := copy(n, i, length(n) - i + 1);
|
||||
{ writeln('** splitFilename ',basename, ' ', extension); }
|
||||
end
|
||||
else
|
||||
begin
|
||||
basename := n;
|
||||
extension := '';
|
||||
end;
|
||||
end;
|
||||
|
||||
function replaceExtension(var n:pathnamestr; newExt:filenamestr):pathnamestr;
|
||||
var basename:filenamestr;
|
||||
ext:filenamestr;
|
||||
begin
|
||||
splitFilename(n, basename, ext);
|
||||
replaceExtension := basename + newExt;
|
||||
end;
|
||||
|
||||
procedure waitForKey; forward;
|
||||
|
||||
procedure listDirectory;
|
||||
var volid:integer;
|
||||
error:integer;
|
||||
index:integer;
|
||||
dirs:DirectorySlot;
|
||||
ftime:DateTime;
|
||||
screenW,screenH:integer;
|
||||
count:integer;
|
||||
begin
|
||||
GetTermSize(screenW, screenH);
|
||||
|
||||
volid := findvolume(DefaultVolume);
|
||||
if volid < 1 then
|
||||
writeln('Volume ', DefaultVolume, ' not found.')
|
||||
else
|
||||
begin
|
||||
count := PageMargin;
|
||||
|
||||
writeln('reading directory of ', DefaultVolume);
|
||||
openvolumeid(volid);
|
||||
readdirfirst(volid, index, dirs, error);
|
||||
while index > 0 do
|
||||
begin
|
||||
if dirs.modTime = 0 then
|
||||
begin
|
||||
ftime.year := 1970;
|
||||
ftime.month := 1;
|
||||
ftime.day := 1;
|
||||
ftime.hours := 0;
|
||||
ftime.minutes := 0;
|
||||
ftime.seconds := 0;
|
||||
end
|
||||
else
|
||||
ftime := GetDateTime(dirs.modTime);
|
||||
writew(dirs.name, 34);
|
||||
writew(DateStr(ftime) + ' ' + TimeStr(ftime,false), 22);
|
||||
writeln(dirs.sizeBytes:12, ' ', dirs.generation);
|
||||
|
||||
count := count + 1;
|
||||
if count >= screenH then
|
||||
begin
|
||||
count := PageMargin;
|
||||
waitForKey;
|
||||
end;
|
||||
|
||||
readdirnext(volid, index, dirs, error);
|
||||
end;
|
||||
closevolumeid(volid);
|
||||
|
||||
if count + MenuHeight >= screenH then
|
||||
waitForKey;
|
||||
end;
|
||||
end;
|
||||
|
||||
function volumeExists(var n:volumenamestr):boolean;
|
||||
var volid:integer;
|
||||
begin
|
||||
volid := findvolume(n);
|
||||
if volid < 1 then
|
||||
volumeExists := false
|
||||
else
|
||||
begin
|
||||
closevolumeid(volid);
|
||||
volumeExists := true;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure listVolumes;
|
||||
var i:integer;
|
||||
begin
|
||||
InitDevices;
|
||||
writeln('Available volumes:');
|
||||
for i := 1 to VolumeCount do
|
||||
writeln(VolumeTable[i].part.name);
|
||||
end;
|
||||
|
||||
procedure changeVolume;
|
||||
var n:volumenamestr;
|
||||
begin
|
||||
listVolumes;
|
||||
write('Enter volume name: ');
|
||||
readln(n);
|
||||
if length(n) > 0 then
|
||||
if volumeExists(n) then
|
||||
SetDefaultVolume(n)
|
||||
else
|
||||
writeln('Volume ', n , ' not found.');
|
||||
end;
|
||||
|
||||
procedure removeFile;
|
||||
var n:filenamestr;
|
||||
error:integer;
|
||||
begin
|
||||
write('File to delete: ');
|
||||
readln(n);
|
||||
|
||||
if length(n) > 0 then
|
||||
begin
|
||||
erase(n, error);
|
||||
|
||||
if error <> 0 then
|
||||
writeln('Error deleting ', n, ': ', ErrorStr(error));
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure renameFile;
|
||||
var n1,n2:filenamestr;
|
||||
error:integer;
|
||||
begin
|
||||
write('File to rename: ');
|
||||
readln(n1);
|
||||
write('New name: ');
|
||||
readln(n2);
|
||||
rename(n1, n2, error);
|
||||
|
||||
if error <> 0 then
|
||||
writeln('Error renaming ', n1, ': ', ErrorStr(error));
|
||||
end;
|
||||
|
||||
procedure copyFile;
|
||||
var n1,n2:filenamestr;
|
||||
error:integer;
|
||||
src,dst:file;
|
||||
ch:char;
|
||||
count:integer;
|
||||
begin
|
||||
write('File to copy: ');
|
||||
readln(n1);
|
||||
write('New file name: ');
|
||||
readln(n2);
|
||||
|
||||
open(src, n1, ModeReadonly);
|
||||
if IOResult(src) <> 0 then
|
||||
begin
|
||||
writeln('Error opening ', n1, ': ', ErrorStr(IOResult(src)));
|
||||
exit;
|
||||
end;
|
||||
|
||||
open(dst, n2, ModeCreate);
|
||||
if IOResult(dst) <> 0 then
|
||||
begin
|
||||
writeln('Error opening ', n2, ': ', ErrorStr(IOResult(dst)));
|
||||
close(src);
|
||||
exit;
|
||||
end;
|
||||
|
||||
write('Copying ',n1, ' to ', n2, '...');
|
||||
count := 0;
|
||||
while not eof(src) do
|
||||
begin
|
||||
read(src,ch); (* not efficient but keep it simple *)
|
||||
write(dst,ch);
|
||||
count := count + 1;
|
||||
if (count and 8191) = 0 then write('.');
|
||||
end;
|
||||
|
||||
close(dst);
|
||||
close(src);
|
||||
|
||||
writeln;
|
||||
end;
|
||||
|
||||
procedure setWorkfile;
|
||||
var n:filenamestr;
|
||||
begin
|
||||
write('Enter workfile name: ');
|
||||
readln(n);
|
||||
|
||||
ShellWorkfile := n;
|
||||
ShellCmd := '';
|
||||
ShellArg := 0;
|
||||
end;
|
||||
|
||||
procedure requireWorkfile;
|
||||
begin
|
||||
while length(ShellWorkFile) = 0 do
|
||||
setWorkfile;
|
||||
end;
|
||||
|
||||
procedure edit(gotoLine:integer);
|
||||
var error:integer;
|
||||
digits:string[10];
|
||||
args:PArgVec;
|
||||
begin
|
||||
requireWorkfile;
|
||||
if gotoLine > 0 then
|
||||
begin
|
||||
str(gotoLine,digits);
|
||||
args[0] := '-l';
|
||||
args[1] := digits;
|
||||
args[2] := ShellWorkFile;
|
||||
PExec(EDITORPROG, args, 3, error);
|
||||
end
|
||||
else
|
||||
PExec2(EDITORPROG, ShellWorkFile, error);
|
||||
writeln('PExec error ', error);
|
||||
end;
|
||||
|
||||
procedure assemble;
|
||||
var filename:filenamestr;
|
||||
error:integer;
|
||||
begin
|
||||
requireWorkfile;
|
||||
filename := replaceExtension(ShellWorkFile, '.s');
|
||||
PExec2(ASMPROG, filename, error);
|
||||
writeln('PExec error ', error);
|
||||
end;
|
||||
|
||||
procedure compile;
|
||||
var filename:filenamestr;
|
||||
error:integer;
|
||||
begin
|
||||
requireWorkfile;
|
||||
filename := replaceExtension(ShellWorkFile, '.pas');
|
||||
PExec3(COMPILERPROG, '-S', filename, error);
|
||||
writeln('PExec error ', error);
|
||||
end;
|
||||
|
||||
procedure build;
|
||||
var filename:filenamestr;
|
||||
error:integer;
|
||||
begin
|
||||
requireWorkfile;
|
||||
filename := replaceExtension(ShellWorkFile, '.pas');
|
||||
PExec2(COMPILERPROG, filename, error);
|
||||
writeln('PExec error ', error);
|
||||
end;
|
||||
|
||||
procedure run;
|
||||
var args:PArgVec;
|
||||
error:integer;
|
||||
prgname:pathnamestr;
|
||||
begin
|
||||
requireWorkfile;
|
||||
prgname := replaceExtension(ShellWorkfile, '.prog');
|
||||
writeln('Running ', prgname);
|
||||
PExec(prgname, args, 0, error);
|
||||
writeln('Pexec failed, error ', error);
|
||||
end;
|
||||
|
||||
procedure krunch;
|
||||
var error:integer;
|
||||
begin
|
||||
PExec2(RECLAIMPROG, DefaultVolume, error);
|
||||
writeln('PExec error ', error);
|
||||
end;
|
||||
|
||||
procedure runProgram;
|
||||
var args:PArgVec;
|
||||
argCount:integer;
|
||||
error:integer;
|
||||
prgname:pathnamestr;
|
||||
a:string;
|
||||
begin
|
||||
write('Enter program file name: ');
|
||||
readln(prgname);
|
||||
|
||||
if length(prgname) > 0 then
|
||||
begin
|
||||
if pos('.', prgname) = 0 then prgname := prgname + '.prog';
|
||||
|
||||
writeln('Enter program arguments line-by-line, empty line to finish.');
|
||||
|
||||
(* entering the arguments line by line is ugly, but it saves us from
|
||||
the hassle of dealing with word boundary detection and quoting *)
|
||||
argCount := 0;
|
||||
repeat
|
||||
write('arg ', argCount + 1, ': ');
|
||||
readln(a);
|
||||
if length(a) > 0 then
|
||||
begin
|
||||
args[argCount] := a;
|
||||
argCount := argCount + 1;
|
||||
end;
|
||||
until (length(a) = 0) or (argCount > PArgMax);
|
||||
|
||||
writeln('Running ', prgname);
|
||||
PExec(prgname, args, argCount, error);
|
||||
writeln('Pexec failed, error ', error);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure showMenu;
|
||||
begin
|
||||
writeln;
|
||||
writeln('W)orkfile: ', ShellWorkfile);
|
||||
writeln('V)olume: ', DefaultVolume);
|
||||
writeln('L)ist directory K)runch volume O)ther program');
|
||||
writeln('D)elete file RenaM)e file coP)y file');
|
||||
writeln('E)dit C)ompile A)ssemble B)uild R)un');
|
||||
write('> ');
|
||||
end;
|
||||
|
||||
procedure command(cmd:char;arg:integer);
|
||||
begin
|
||||
case cmd of
|
||||
'L': listDirectory;
|
||||
'V': changeVolume;
|
||||
'W': setWorkfile;
|
||||
'R': run;
|
||||
'A': assemble;
|
||||
'C': compile;
|
||||
'B': build;
|
||||
'E': edit(arg);
|
||||
'D': removeFile;
|
||||
'M': renameFile;
|
||||
'P': copyFile;
|
||||
'K': krunch;
|
||||
'O': runProgram;
|
||||
else ;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure waitForKey;
|
||||
var c:char;
|
||||
begin
|
||||
writeln;
|
||||
writeln('-- press any key to continue --');
|
||||
c := conin;
|
||||
end;
|
||||
|
||||
begin
|
||||
if length(DefaultVolume) = 0 then
|
||||
SetDefaultVolume('SYSTEM');
|
||||
|
||||
checkClock;
|
||||
|
||||
if length(ShellCmd) > 0 then
|
||||
begin
|
||||
if ShellCmd[1] = 'W' then
|
||||
begin
|
||||
waitForKey;
|
||||
delete(Shellcmd,1,1);
|
||||
end;
|
||||
|
||||
if length(ShellCmd) > 0 then
|
||||
command(ShellCmd[1], ShellArg);
|
||||
|
||||
ShellCmd := '';
|
||||
end;
|
||||
|
||||
while true do
|
||||
begin
|
||||
showMenu;
|
||||
read(cmd);
|
||||
writeln;
|
||||
command(Upcase(cmd), ShellArg);
|
||||
end;
|
||||
end.
|
||||
431
progs/xfer.pas
Normal file
431
progs/xfer.pas
Normal file
|
|
@ -0,0 +1,431 @@
|
|||
(* Copyright 2021-2024 Sebastian Lederer. See the file LICENSE.md for details *)
|
||||
program xfer;
|
||||
|
||||
const CksumPattern = $AFFECAFE;
|
||||
SOH = #1;
|
||||
STX = #2;
|
||||
EOT = #4;
|
||||
ENQ = #5;
|
||||
ACK = #6;
|
||||
BEL = #7;
|
||||
NAK = #21;
|
||||
|
||||
TimeoutTicks = 200;
|
||||
|
||||
var blockNo:integer;
|
||||
buf:^string;
|
||||
invalid:boolean;
|
||||
cmd:char;
|
||||
filename:string;
|
||||
size:integer;
|
||||
done:boolean;
|
||||
xferFile:file;
|
||||
|
||||
function calcChksum(last,this:integer):integer;
|
||||
begin
|
||||
calcChksum := ((last + this) xor CksumPattern) shl 1;
|
||||
end;
|
||||
|
||||
function readword:integer;
|
||||
var b3,b2,b1,b0:char;
|
||||
begin
|
||||
b3 := conin;
|
||||
b2 := conin;
|
||||
b1 := conin;
|
||||
b0 := conin;
|
||||
|
||||
readword := (ord(b3) shl 24) or
|
||||
(ord(b2) shl 16) or
|
||||
(ord(b1) shl 8) or
|
||||
ord(b0);
|
||||
end;
|
||||
|
||||
procedure serReadBlock(var success:boolean);
|
||||
var w0,w1,w2,w3,w4,w5,w6,w7,w8:integer;
|
||||
chksum:integer;
|
||||
s:integer;
|
||||
|
||||
procedure writeByte(b:char);
|
||||
begin
|
||||
if size <> 0 then
|
||||
begin
|
||||
write(xferFile,b);
|
||||
size := size - 1;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure appendWordToFile(w:integer);
|
||||
var b3,b2,b1,b0:char;
|
||||
begin
|
||||
b0 := chr(w and 255);
|
||||
w := w shr 8;
|
||||
|
||||
b1 := chr(w and 255);
|
||||
w := w shr 8;
|
||||
|
||||
b2 := chr(w and 255);
|
||||
w := w shr 8;
|
||||
|
||||
b3 := chr(w);
|
||||
|
||||
writeByte(b3);
|
||||
writeByte(b2);
|
||||
writeByte(b1);
|
||||
writeByte(b0);
|
||||
end;
|
||||
|
||||
procedure calcChksum(d:integer);
|
||||
begin
|
||||
chksum := ((chksum + d) xor CksumPattern) shl 1;
|
||||
end;
|
||||
|
||||
begin
|
||||
chksum := 0;
|
||||
|
||||
w0 := readword;
|
||||
w1 := readword;
|
||||
w2 := readword;
|
||||
w3 := readword;
|
||||
w4 := readword;
|
||||
w5 := readword;
|
||||
w6 := readword;
|
||||
w7 := readword;
|
||||
|
||||
s := readword;
|
||||
|
||||
calcChksum(w0);
|
||||
calcChksum(w1);
|
||||
calcChksum(w2);
|
||||
calcChksum(w3);
|
||||
calcChksum(w4);
|
||||
calcChksum(w5);
|
||||
calcChksum(w6);
|
||||
calcChksum(w7);
|
||||
|
||||
if s <> chksum then
|
||||
begin
|
||||
success := false;
|
||||
write(NAK);
|
||||
{ writeln('invalid chksum ', s, ' ', chksum); }
|
||||
end
|
||||
else
|
||||
begin
|
||||
success := true;
|
||||
appendWordToFile(w0);
|
||||
appendWordToFile(w1);
|
||||
appendWordToFile(w2);
|
||||
appendWordToFile(w3);
|
||||
appendWordToFile(w4);
|
||||
appendWordToFile(w5);
|
||||
appendWordToFile(w6);
|
||||
appendWordToFile(w7);
|
||||
blockNo := blockNo + 1;
|
||||
write(ACK);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure waitForByte(var byteReceived:char; var timeoutReached:boolean);
|
||||
var ticks:integer;
|
||||
done:boolean;
|
||||
begin
|
||||
timeoutReached := true;
|
||||
ticks := getticks;
|
||||
done := false;
|
||||
|
||||
repeat
|
||||
if conavail then
|
||||
begin
|
||||
done := true;
|
||||
timeoutReached := false;
|
||||
byteReceived := conin;
|
||||
end;
|
||||
until done or (getticks > ticks + TimeoutTicks);
|
||||
end;
|
||||
|
||||
procedure waitForHeader(var invalid:boolean);
|
||||
var done:boolean;
|
||||
timedOut:boolean;
|
||||
ch:char;
|
||||
begin
|
||||
waitForByte(ch, timedOut);
|
||||
invalid := (ch <> STX) or timedOut;
|
||||
end;
|
||||
|
||||
procedure receiveHeader(var invalid:boolean);
|
||||
var ch:char;
|
||||
timedOut:boolean;
|
||||
w:integer;
|
||||
cksum:integer;
|
||||
begin
|
||||
{ send protocol version, then wait for size header }
|
||||
write('1');
|
||||
|
||||
waitForByte(ch, timedOut);
|
||||
if timedOut or (ch <> SOH) then
|
||||
begin
|
||||
invalid := true;
|
||||
exit;
|
||||
end;
|
||||
|
||||
cksum := 0;
|
||||
|
||||
w := readword;
|
||||
cksum := readword;
|
||||
|
||||
if w <> (not cksum) then
|
||||
begin
|
||||
write(NAK);
|
||||
w := 0;
|
||||
writeln('h chksum error');
|
||||
end
|
||||
else
|
||||
write(ACK);
|
||||
|
||||
if w > 0 then
|
||||
begin
|
||||
size := w;
|
||||
waitForHeader(invalid);
|
||||
end
|
||||
else
|
||||
invalid := true;
|
||||
end;
|
||||
|
||||
procedure receiveFile;
|
||||
var ch:char;
|
||||
invalid, timedOut:boolean;
|
||||
ok:boolean;
|
||||
done:boolean;
|
||||
errorCount:integer;
|
||||
begin
|
||||
if length(filename) = 0 then
|
||||
begin
|
||||
writeln('Filename not set.');
|
||||
exit;
|
||||
end;
|
||||
|
||||
errorCount := 0;
|
||||
|
||||
waitForByte(ch, timedOut);
|
||||
if timedOut then
|
||||
begin
|
||||
writeln('Timeout waiting for transmission start (ENQ or STX).');
|
||||
exit;
|
||||
end;
|
||||
|
||||
if ch = ENQ then
|
||||
receiveHeader(invalid)
|
||||
else
|
||||
if ch = STX then
|
||||
begin
|
||||
size := -1;
|
||||
invalid := false;
|
||||
end
|
||||
else
|
||||
invalid := true;
|
||||
|
||||
if not invalid then
|
||||
begin
|
||||
open(xferFile, filename, ModeOverwrite);
|
||||
done := false;
|
||||
repeat
|
||||
serReadBlock(ok);
|
||||
if not ok then errorCount := errorCount + 1;
|
||||
|
||||
waitForByte(ch, timedOut);
|
||||
if timedOut then
|
||||
writeln('Timeout waiting for next block (STX)');
|
||||
if ch = EOT then
|
||||
done := true
|
||||
else
|
||||
if ch <> STX then
|
||||
begin
|
||||
writeln('Invalid header byte (expected STX)');
|
||||
done := true;
|
||||
end;
|
||||
until done or timedOut;
|
||||
|
||||
close(xferFile);
|
||||
|
||||
writeln(blockNo, ' blocks received, ', errorCount, ' checksum errors. ', ord(ch));
|
||||
end
|
||||
else
|
||||
writeln('Invalid or no header received.', size);
|
||||
end;
|
||||
|
||||
function getWordFromFile:integer;
|
||||
|
||||
function getCharFromFile:integer;
|
||||
var c:char;
|
||||
begin
|
||||
if size > 0 then
|
||||
begin
|
||||
read(xferFile,c);
|
||||
size := size - 1;
|
||||
end
|
||||
else
|
||||
c := #0;
|
||||
|
||||
getCharFromFile := ord(c);
|
||||
end;
|
||||
|
||||
begin
|
||||
getWordFromFile := getCharFromFile shl 8;
|
||||
getWordFromFile := (getWordFromFile or getCharFromFile) shl 8;
|
||||
getWordFromFile := (getWordFromFile or getCharFromFile) shl 8;
|
||||
getWordFromFile := (getWordFromFile or getCharFromFile);
|
||||
end;
|
||||
|
||||
procedure sendword(w:integer);
|
||||
var b3,b2,b1,b0:char;
|
||||
begin
|
||||
b0 := chr(w and 255);
|
||||
w := w shr 8;
|
||||
|
||||
b1 := chr(w and 255);
|
||||
w := w shr 8;
|
||||
|
||||
b2 := chr(w and 255);
|
||||
w := w shr 8;
|
||||
|
||||
b3 := chr(w and 255);
|
||||
|
||||
write(b3,b2,b1,b0);
|
||||
end;
|
||||
|
||||
procedure sendFile;
|
||||
var ch:char;
|
||||
w,cksum:integer;
|
||||
wordCount:integer;
|
||||
lastSize,lastPos:integer;
|
||||
timedOut:boolean;
|
||||
done:boolean;
|
||||
begin
|
||||
if length(filename) = 0 then
|
||||
begin
|
||||
writeln('Filename not set.');
|
||||
exit;
|
||||
end;
|
||||
|
||||
{ wait for start byte }
|
||||
ch := conin;
|
||||
if ch <> BEL then
|
||||
begin
|
||||
writeln('Invalid start character received.');
|
||||
exit;
|
||||
end;
|
||||
|
||||
open(xferFile, filename, ModeReadonly);
|
||||
if IOResult(xferFile) <> 0 then
|
||||
begin
|
||||
writeln('Error opening file: ', ErrorStr(IOResult(xferFile)));
|
||||
exit;
|
||||
end;
|
||||
|
||||
size := filesize(xferFile);
|
||||
done := false;
|
||||
|
||||
{ send size header: SOH, size word, inverted size word }
|
||||
write(SOH);
|
||||
sendword(size);
|
||||
sendword(not size);
|
||||
|
||||
{ check for ACK }
|
||||
waitForByte(ch, timedOut);
|
||||
|
||||
if timedOut then
|
||||
writeln('Timeout sending size header ')
|
||||
else
|
||||
if ch <> ACK then
|
||||
writeln('Error sending size header ', ord(ch))
|
||||
else
|
||||
repeat
|
||||
lastPos := filepos(xferFile);
|
||||
lastSize := size;
|
||||
|
||||
write(STX);
|
||||
{ send a block: STX, 8 words, checksum word }
|
||||
cksum := 0;
|
||||
for wordCount := 1 to 8 do
|
||||
begin
|
||||
w := getWordFromFile;
|
||||
cksum := calcChkSum(cksum, w);
|
||||
sendword(w);
|
||||
end;
|
||||
sendword(cksum);
|
||||
|
||||
{ check for ACK/NAK }
|
||||
waitForByte(ch, timedOut);
|
||||
if timedOut then
|
||||
begin
|
||||
writeln('Timeout waiting for ACK');
|
||||
done := true;
|
||||
end
|
||||
else
|
||||
if ch = NAK then
|
||||
begin
|
||||
seek(xferFile, lastPos);
|
||||
size := lastSize;
|
||||
end
|
||||
else
|
||||
if ch = ACK then
|
||||
begin
|
||||
if size = 0 then done := true;
|
||||
end
|
||||
else
|
||||
begin
|
||||
writeln('Invalid reply after sending block');
|
||||
done := true;
|
||||
end;
|
||||
until done;
|
||||
write(EOT);
|
||||
close(xferFile);
|
||||
end;
|
||||
|
||||
procedure setFilename;
|
||||
begin
|
||||
write('Filename> ');
|
||||
readln(filename);
|
||||
end;
|
||||
|
||||
procedure listDirectory;
|
||||
var volid:integer;
|
||||
error:integer;
|
||||
index:integer;
|
||||
dirs:DirectorySlot;
|
||||
begin
|
||||
volid := findvolume(DefaultVolume);
|
||||
if volid < 1 then
|
||||
writeln('Volume ', DefaultVolume, ' not found.')
|
||||
else
|
||||
begin
|
||||
openvolumeid(volid);
|
||||
readdirfirst(volid, index, dirs, error);
|
||||
while (index > 0) and (error = 0) do
|
||||
begin
|
||||
writeln(dirs.name);
|
||||
readdirnext(volid, index, dirs, error);
|
||||
end;
|
||||
end;
|
||||
writeln;
|
||||
end;
|
||||
|
||||
begin
|
||||
writeln('L) upload (receive) D) download (send).');
|
||||
writeln('S) set filename Y) directory X) exit');
|
||||
|
||||
done := false;
|
||||
|
||||
repeat
|
||||
write('> ');
|
||||
read(cmd);
|
||||
writeln;
|
||||
case upcase(cmd) of
|
||||
'L': receiveFile;
|
||||
'D': sendFile;
|
||||
'S': setFilename;
|
||||
'X': done := true;
|
||||
'Y': listDirectory;
|
||||
else writeln('?');
|
||||
end;
|
||||
until done;
|
||||
end.
|
||||
218
rtl/arty-a7/Arty-A7-35-Master.xdc
Normal file
218
rtl/arty-a7/Arty-A7-35-Master.xdc
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
## This file is a general .xdc for the Arty A7-35 Rev. D
|
||||
## To use it in a project:
|
||||
## - uncomment the lines corresponding to used pins
|
||||
## - rename the used ports (in each line, after get_ports) according to the top level signal names in the project
|
||||
|
||||
## Clock signal
|
||||
set_property -dict {PACKAGE_PIN E3 IOSTANDARD LVCMOS33} [get_ports clk]
|
||||
create_clock -period 10.000 -name sys_clk_pin -waveform {0.000 5.000} -add [get_ports clk]
|
||||
|
||||
## Switches
|
||||
set_property -dict {PACKAGE_PIN A8 IOSTANDARD LVCMOS33} [get_ports sw0]
|
||||
set_property -dict {PACKAGE_PIN C11 IOSTANDARD LVCMOS33} [get_ports sw1]
|
||||
#set_property -dict { PACKAGE_PIN C10 IOSTANDARD LVCMOS33 } [get_ports { sw[2] }]; #IO_L13N_T2_MRCC_16 Sch=sw[2]
|
||||
#set_property -dict { PACKAGE_PIN A10 IOSTANDARD LVCMOS33 } [get_ports { sw[3] }]; #IO_L14P_T2_SRCC_16 Sch=sw[3]
|
||||
|
||||
## RGB LEDs
|
||||
#set_property -dict { PACKAGE_PIN E1 IOSTANDARD LVCMOS33 } [get_ports { led0_b }]; #IO_L18N_T2_35 Sch=led0_b
|
||||
#set_property -dict { PACKAGE_PIN F6 IOSTANDARD LVCMOS33 } [get_ports { led0_g }]; #IO_L19N_T3_VREF_35 Sch=led0_g
|
||||
#set_property -dict { PACKAGE_PIN G6 IOSTANDARD LVCMOS33 } [get_ports { led0_r }]; #IO_L19P_T3_35 Sch=led0_r
|
||||
#set_property -dict { PACKAGE_PIN G4 IOSTANDARD LVCMOS33 } [get_ports { led1_b }]; #IO_L20P_T3_35 Sch=led1_b
|
||||
#set_property -dict { PACKAGE_PIN J4 IOSTANDARD LVCMOS33 } [get_ports { led1_g }]; #IO_L21P_T3_DQS_35 Sch=led1_g
|
||||
#set_property -dict { PACKAGE_PIN G3 IOSTANDARD LVCMOS33 } [get_ports { led1_r }]; #IO_L20N_T3_35 Sch=led1_r
|
||||
#set_property -dict { PACKAGE_PIN H4 IOSTANDARD LVCMOS33 } [get_ports { led2_b }]; #IO_L21N_T3_DQS_35 Sch=led2_b
|
||||
#set_property -dict { PACKAGE_PIN J2 IOSTANDARD LVCMOS33 } [get_ports { led2_g }]; #IO_L22N_T3_35 Sch=led2_g
|
||||
#set_property -dict { PACKAGE_PIN J3 IOSTANDARD LVCMOS33 } [get_ports { led2_r }]; #IO_L22P_T3_35 Sch=led2_r
|
||||
#set_property -dict { PACKAGE_PIN K2 IOSTANDARD LVCMOS33 } [get_ports { led3_b }]; #IO_L23P_T3_35 Sch=led3_b
|
||||
#set_property -dict { PACKAGE_PIN H6 IOSTANDARD LVCMOS33 } [get_ports { led3_g }]; #IO_L24P_T3_35 Sch=led3_g
|
||||
#set_property -dict { PACKAGE_PIN K1 IOSTANDARD LVCMOS33 } [get_ports { led3_r }]; #IO_L23N_T3_35 Sch=led3_r
|
||||
|
||||
## LEDs
|
||||
set_property -dict {PACKAGE_PIN H5 IOSTANDARD LVCMOS33} [get_ports led0]
|
||||
set_property -dict {PACKAGE_PIN J5 IOSTANDARD LVCMOS33} [get_ports led1]
|
||||
set_property -dict {PACKAGE_PIN T9 IOSTANDARD LVCMOS33} [get_ports led2]
|
||||
set_property -dict {PACKAGE_PIN T10 IOSTANDARD LVCMOS33} [get_ports led3]
|
||||
|
||||
## Buttons
|
||||
set_property -dict {PACKAGE_PIN D9 IOSTANDARD LVCMOS33} [get_ports btn0]
|
||||
#set_property -dict { PACKAGE_PIN C9 IOSTANDARD LVCMOS33 } [get_ports { btn1 }]; #IO_L11P_T1_SRCC_16 Sch=btn[1]
|
||||
#set_property -dict { PACKAGE_PIN B9 IOSTANDARD LVCMOS33 } [get_ports { btn[2] }]; #IO_L11N_T1_SRCC_16 Sch=btn[2]
|
||||
#set_property -dict { PACKAGE_PIN B8 IOSTANDARD LVCMOS33 } [get_ports { btn[3] }]; #IO_L12P_T1_MRCC_16 Sch=btn[3]
|
||||
|
||||
## Pmod Header JA
|
||||
set_property -dict {PACKAGE_PIN G13 IOSTANDARD LVCMOS33} [get_ports sd_cs_n]
|
||||
set_property -dict {PACKAGE_PIN B11 IOSTANDARD LVCMOS33} [get_ports sd_mosi]
|
||||
set_property -dict {PACKAGE_PIN A11 IOSTANDARD LVCMOS33} [get_ports sd_miso]
|
||||
set_property -dict {PACKAGE_PIN D12 IOSTANDARD LVCMOS33} [get_ports sd_sck]
|
||||
#set_property -dict { PACKAGE_PIN D13 IOSTANDARD LVCMOS33 } [get_ports { sd_dat1 }]; #IO_L6N_T0_VREF_15 Sch=ja[7]
|
||||
#set_property -dict { PACKAGE_PIN B18 IOSTANDARD LVCMOS33 } [get_ports { sd_dat2 }]; #IO_L10P_T1_AD11P_15 Sch=ja[8]
|
||||
set_property -dict {PACKAGE_PIN A18 IOSTANDARD LVCMOS33} [get_ports sd_cd]
|
||||
#set_property -dict { PACKAGE_PIN K16 IOSTANDARD LVCMOS33 } [get_ports { sd_nc }]; #IO_25_15 Sch=ja[10]
|
||||
|
||||
###Pmod Header JB
|
||||
set_property -dict {PACKAGE_PIN E15 IOSTANDARD LVCMOS33} [get_ports {VGA_R[0]}]
|
||||
set_property -dict {PACKAGE_PIN E16 IOSTANDARD LVCMOS33} [get_ports {VGA_R[1]}]
|
||||
set_property -dict {PACKAGE_PIN D15 IOSTANDARD LVCMOS33} [get_ports {VGA_R[2]}]
|
||||
set_property -dict {PACKAGE_PIN C15 IOSTANDARD LVCMOS33} [get_ports {VGA_R[3]}]
|
||||
set_property -dict {PACKAGE_PIN J17 IOSTANDARD LVCMOS33} [get_ports {VGA_B[0]}]
|
||||
set_property -dict {PACKAGE_PIN J18 IOSTANDARD LVCMOS33} [get_ports {VGA_B[1]}]
|
||||
set_property -dict {PACKAGE_PIN K15 IOSTANDARD LVCMOS33} [get_ports {VGA_B[2]}]
|
||||
set_property -dict {PACKAGE_PIN J15 IOSTANDARD LVCMOS33} [get_ports {VGA_B[3]}]
|
||||
|
||||
###Pmod Header JC
|
||||
set_property -dict {PACKAGE_PIN U12 IOSTANDARD LVCMOS33} [get_ports {VGA_G[0]}]
|
||||
set_property -dict {PACKAGE_PIN V12 IOSTANDARD LVCMOS33} [get_ports {VGA_G[1]}]
|
||||
set_property -dict {PACKAGE_PIN V10 IOSTANDARD LVCMOS33} [get_ports {VGA_G[2]}]
|
||||
set_property -dict {PACKAGE_PIN V11 IOSTANDARD LVCMOS33} [get_ports {VGA_G[3]}]
|
||||
set_property -dict {PACKAGE_PIN U14 IOSTANDARD LVCMOS33} [get_ports VGA_HS_O]
|
||||
set_property -dict {PACKAGE_PIN V14 IOSTANDARD LVCMOS33} [get_ports VGA_VS_O]
|
||||
#set_property -dict { PACKAGE_PIN T13 IOSTANDARD LVCMOS33 } [get_ports { jc[6] }]; #IO_L23P_T3_A03_D19_14 Sch=jc_p[4]
|
||||
#set_property -dict { PACKAGE_PIN U13 IOSTANDARD LVCMOS33 } [get_ports { jc[7] }]; #IO_L23N_T3_A02_D18_14 Sch=jc_n[4]
|
||||
|
||||
## Pmod Header JD
|
||||
#set_property -dict { PACKAGE_PIN D4 IOSTANDARD LVCMOS33 } [get_ports { jd[0] }]; #IO_L11N_T1_SRCC_35 Sch=jd[1]
|
||||
#set_property -dict { PACKAGE_PIN D3 IOSTANDARD LVCMOS33 } [get_ports { jd[1] }]; #IO_L12N_T1_MRCC_35 Sch=jd[2]
|
||||
#set_property -dict { PACKAGE_PIN F4 IOSTANDARD LVCMOS33 } [get_ports { jd[2] }]; #IO_L13P_T2_MRCC_35 Sch=jd[3]
|
||||
#set_property -dict { PACKAGE_PIN F3 IOSTANDARD LVCMOS33 } [get_ports { jd[3] }]; #IO_L13N_T2_MRCC_35 Sch=jd[4]
|
||||
#set_property -dict { PACKAGE_PIN E2 IOSTANDARD LVCMOS33 } [get_ports { jd[4] }]; #IO_L14P_T2_SRCC_35 Sch=jd[7]
|
||||
#set_property -dict { PACKAGE_PIN D2 IOSTANDARD LVCMOS33 } [get_ports { jd[5] }]; #IO_L14N_T2_SRCC_35 Sch=jd[8]
|
||||
#set_property -dict { PACKAGE_PIN H2 IOSTANDARD LVCMOS33 } [get_ports { jd[6] }]; #IO_L15P_T2_DQS_35 Sch=jd[9]
|
||||
#set_property -dict { PACKAGE_PIN G2 IOSTANDARD LVCMOS33 } [get_ports { jd[7] }]; #IO_L15N_T2_DQS_35 Sch=jd[10]
|
||||
|
||||
## USB-UART Interface
|
||||
set_property -dict {PACKAGE_PIN D10 IOSTANDARD LVCMOS33} [get_ports uart_rxd_out]
|
||||
set_property -dict {PACKAGE_PIN A9 IOSTANDARD LVCMOS33} [get_ports uart_txd_in]
|
||||
|
||||
## ChipKit Outer Digital Header
|
||||
#set_property -dict { PACKAGE_PIN V15 IOSTANDARD LVCMOS33 } [get_ports { ck_io0 }]; #IO_L16P_T2_CSI_B_14 Sch=ck_io[0]
|
||||
#set_property -dict { PACKAGE_PIN U16 IOSTANDARD LVCMOS33 } [get_ports { ck_io1 }]; #IO_L18P_T2_A12_D28_14 Sch=ck_io[1]
|
||||
#set_property -dict { PACKAGE_PIN P14 IOSTANDARD LVCMOS33 } [get_ports { ck_io2 }]; #IO_L8N_T1_D12_14 Sch=ck_io[2]
|
||||
#set_property -dict { PACKAGE_PIN T11 IOSTANDARD LVCMOS33 } [get_ports { ck_io3 }]; #IO_L19P_T3_A10_D26_14 Sch=ck_io[3]
|
||||
#set_property -dict { PACKAGE_PIN R12 IOSTANDARD LVCMOS33 } [get_ports { ck_io4 }]; #IO_L5P_T0_D06_14 Sch=ck_io[4]
|
||||
#set_property -dict { PACKAGE_PIN T14 IOSTANDARD LVCMOS33 } [get_ports { ck_io5 }]; #IO_L14P_T2_SRCC_14 Sch=ck_io[5]
|
||||
#set_property -dict { PACKAGE_PIN T15 IOSTANDARD LVCMOS33 } [get_ports { ck_io6 }]; #IO_L14N_T2_SRCC_14 Sch=ck_io[6]
|
||||
#set_property -dict { PACKAGE_PIN T16 IOSTANDARD LVCMOS33 } [get_ports { ck_io7 }]; #IO_L15N_T2_DQS_DOUT_CSO_B_14 Sch=ck_io[7]
|
||||
#set_property -dict { PACKAGE_PIN N15 IOSTANDARD LVCMOS33 } [get_ports { ck_io8 }]; #IO_L11P_T1_SRCC_14 Sch=ck_io[8]
|
||||
#set_property -dict { PACKAGE_PIN M16 IOSTANDARD LVCMOS33 } [get_ports { ck_io9 }]; #IO_L10P_T1_D14_14 Sch=ck_io[9]
|
||||
#set_property -dict { PACKAGE_PIN V17 IOSTANDARD LVCMOS33 } [get_ports { ck_io10 }]; #IO_L18N_T2_A11_D27_14 Sch=ck_io[10]
|
||||
#set_property -dict { PACKAGE_PIN U18 IOSTANDARD LVCMOS33 } [get_ports { ck_io11 }]; #IO_L17N_T2_A13_D29_14 Sch=ck_io[11]
|
||||
#set_property -dict { PACKAGE_PIN R17 IOSTANDARD LVCMOS33 } [get_ports { ck_io12 }]; #IO_L12N_T1_MRCC_14 Sch=ck_io[12]
|
||||
#set_property -dict { PACKAGE_PIN P17 IOSTANDARD LVCMOS33 } [get_ports { ck_io13 }]; #IO_L12P_T1_MRCC_14 Sch=ck_io[13]
|
||||
|
||||
## ChipKit Inner Digital Header
|
||||
#set_property -dict { PACKAGE_PIN U11 IOSTANDARD LVCMOS33 } [get_ports { ck_io26 }]; #IO_L19N_T3_A09_D25_VREF_14 Sch=ck_io[26]
|
||||
#set_property -dict { PACKAGE_PIN V16 IOSTANDARD LVCMOS33 } [get_ports { ck_io27 }]; #IO_L16N_T2_A15_D31_14 Sch=ck_io[27]
|
||||
#set_property -dict { PACKAGE_PIN M13 IOSTANDARD LVCMOS33 } [get_ports { ck_io28 }]; #IO_L6N_T0_D08_VREF_14 Sch=ck_io[28]
|
||||
#set_property -dict { PACKAGE_PIN R10 IOSTANDARD LVCMOS33 } [get_ports { ck_io29 }]; #IO_25_14 Sch=ck_io[29]
|
||||
#set_property -dict { PACKAGE_PIN R11 IOSTANDARD LVCMOS33 } [get_ports { ck_io30 }]; #IO_0_14 Sch=ck_io[30]
|
||||
#set_property -dict { PACKAGE_PIN R13 IOSTANDARD LVCMOS33 } [get_ports { ck_io31 }]; #IO_L5N_T0_D07_14 Sch=ck_io[31]
|
||||
#set_property -dict { PACKAGE_PIN R15 IOSTANDARD LVCMOS33 } [get_ports { ck_io32 }]; #IO_L13N_T2_MRCC_14 Sch=ck_io[32]
|
||||
#set_property -dict { PACKAGE_PIN P15 IOSTANDARD LVCMOS33 } [get_ports { ck_io33 }]; #IO_L13P_T2_MRCC_14 Sch=ck_io[33]
|
||||
#set_property -dict { PACKAGE_PIN R16 IOSTANDARD LVCMOS33 } [get_ports { ck_io34 }]; #IO_L15P_T2_DQS_RDWR_B_14 Sch=ck_io[34]
|
||||
#set_property -dict { PACKAGE_PIN N16 IOSTANDARD LVCMOS33 } [get_ports { ck_io35 }]; #IO_L11N_T1_SRCC_14 Sch=ck_io[35]
|
||||
#set_property -dict { PACKAGE_PIN N14 IOSTANDARD LVCMOS33 } [get_ports { ck_io36 }]; #IO_L8P_T1_D11_14 Sch=ck_io[36]
|
||||
#set_property -dict { PACKAGE_PIN U17 IOSTANDARD LVCMOS33 } [get_ports { ck_io37 }]; #IO_L17P_T2_A14_D30_14 Sch=ck_io[37]
|
||||
#set_property -dict { PACKAGE_PIN T18 IOSTANDARD LVCMOS33 } [get_ports { ck_io38 }]; #IO_L7N_T1_D10_14 Sch=ck_io[38]
|
||||
#set_property -dict { PACKAGE_PIN R18 IOSTANDARD LVCMOS33 } [get_ports { ck_io39 }]; #IO_L7P_T1_D09_14 Sch=ck_io[39]
|
||||
#set_property -dict { PACKAGE_PIN P18 IOSTANDARD LVCMOS33 } [get_ports { ck_io40 }]; #IO_L9N_T1_DQS_D13_14 Sch=ck_io[40]
|
||||
#set_property -dict { PACKAGE_PIN N17 IOSTANDARD LVCMOS33 } [get_ports { ck_io41 }]; #IO_L9P_T1_DQS_14 Sch=ck_io[41]
|
||||
|
||||
## ChipKit Outer Analog Header - as Single-Ended Analog Inputs
|
||||
## NOTE: These ports can be used as single-ended analog inputs with voltages from 0-3.3V (ChipKit analog pins A0-A5) or as digital I/O.
|
||||
## WARNING: Do not use both sets of constraints at the same time!
|
||||
## NOTE: The following constraints should be used with the XADC IP core when using these ports as analog inputs.
|
||||
#set_property -dict { PACKAGE_PIN C5 IOSTANDARD LVCMOS33 } [get_ports { vaux4_n }]; #IO_L1N_T0_AD4N_35 Sch=ck_an_n[0] ChipKit pin=A0
|
||||
#set_property -dict { PACKAGE_PIN C6 IOSTANDARD LVCMOS33 } [get_ports { vaux4_p }]; #IO_L1P_T0_AD4P_35 Sch=ck_an_p[0] ChipKit pin=A0
|
||||
#set_property -dict { PACKAGE_PIN A5 IOSTANDARD LVCMOS33 } [get_ports { vaux5_n }]; #IO_L3N_T0_DQS_AD5N_35 Sch=ck_an_n[1] ChipKit pin=A1
|
||||
#set_property -dict { PACKAGE_PIN A6 IOSTANDARD LVCMOS33 } [get_ports { vaux5_p }]; #IO_L3P_T0_DQS_AD5P_35 Sch=ck_an_p[1] ChipKit pin=A1
|
||||
#set_property -dict { PACKAGE_PIN B4 IOSTANDARD LVCMOS33 } [get_ports { vaux6_n }]; #IO_L7N_T1_AD6N_35 Sch=ck_an_n[2] ChipKit pin=A2
|
||||
#set_property -dict { PACKAGE_PIN C4 IOSTANDARD LVCMOS33 } [get_ports { vaux6_p }]; #IO_L7P_T1_AD6P_35 Sch=ck_an_p[2] ChipKit pin=A2
|
||||
#set_property -dict { PACKAGE_PIN A1 IOSTANDARD LVCMOS33 } [get_ports { vaux7_n }]; #IO_L9N_T1_DQS_AD7N_35 Sch=ck_an_n[3] ChipKit pin=A3
|
||||
#set_property -dict { PACKAGE_PIN B1 IOSTANDARD LVCMOS33 } [get_ports { vaux7_p }]; #IO_L9P_T1_DQS_AD7P_35 Sch=ck_an_p[3] ChipKit pin=A3
|
||||
#set_property -dict { PACKAGE_PIN B2 IOSTANDARD LVCMOS33 } [get_ports { vaux15_n }]; #IO_L10N_T1_AD15N_35 Sch=ck_an_n[4] ChipKit pin=A4
|
||||
#set_property -dict { PACKAGE_PIN B3 IOSTANDARD LVCMOS33 } [get_ports { vaux15_p }]; #IO_L10P_T1_AD15P_35 Sch=ck_an_p[4] ChipKit pin=A4
|
||||
#set_property -dict { PACKAGE_PIN C14 IOSTANDARD LVCMOS33 } [get_ports { vaux0_n }]; #IO_L1N_T0_AD0N_15 Sch=ck_an_n[5] ChipKit pin=A5
|
||||
#set_property -dict { PACKAGE_PIN D14 IOSTANDARD LVCMOS33 } [get_ports { vaux0_p }]; #IO_L1P_T0_AD0P_15 Sch=ck_an_p[5] ChipKit pin=A5
|
||||
## ChipKit Outer Analog Header - as Digital I/O
|
||||
## NOTE: the following constraints should be used when using these ports as digital I/O.
|
||||
#set_property -dict { PACKAGE_PIN F5 IOSTANDARD LVCMOS33 } [get_ports { ck_a0 }]; #IO_0_35 Sch=ck_a[0] ChipKit pin=A0
|
||||
#set_property -dict { PACKAGE_PIN D8 IOSTANDARD LVCMOS33 } [get_ports { ck_a1 }]; #IO_L4P_T0_35 Sch=ck_a[1] ChipKit pin=A1
|
||||
#set_property -dict { PACKAGE_PIN C7 IOSTANDARD LVCMOS33 } [get_ports { ck_a2 }]; #IO_L4N_T0_35 Sch=ck_a[2] ChipKit pin=A2
|
||||
#set_property -dict { PACKAGE_PIN E7 IOSTANDARD LVCMOS33 } [get_ports { ck_a3 }]; #IO_L6P_T0_35 Sch=ck_a[3] ChipKit pin=A3
|
||||
#set_property -dict { PACKAGE_PIN D7 IOSTANDARD LVCMOS33 } [get_ports { ck_a4 }]; #IO_L6N_T0_VREF_35 Sch=ck_a[4] ChipKit pin=A4
|
||||
#set_property -dict { PACKAGE_PIN D5 IOSTANDARD LVCMOS33 } [get_ports { ck_a5 }]; #IO_L11P_T1_SRCC_35 Sch=ck_a[5] ChipKit pin=A5
|
||||
|
||||
## ChipKit Inner Analog Header - as Differential Analog Inputs
|
||||
## NOTE: These ports can be used as differential analog inputs with voltages from 0-1.0V (ChipKit Analog pins A6-A11) or as digital I/O.
|
||||
## WARNING: Do not use both sets of constraints at the same time!
|
||||
## NOTE: The following constraints should be used with the XADC core when using these ports as analog inputs.
|
||||
#set_property -dict { PACKAGE_PIN B7 IOSTANDARD LVCMOS33 } [get_ports { vaux12_p }]; #IO_L2P_T0_AD12P_35 Sch=ad_p[12] ChipKit pin=A6
|
||||
#set_property -dict { PACKAGE_PIN B6 IOSTANDARD LVCMOS33 } [get_ports { vaux12_n }]; #IO_L2N_T0_AD12N_35 Sch=ad_n[12] ChipKit pin=A7
|
||||
#set_property -dict { PACKAGE_PIN E6 IOSTANDARD LVCMOS33 } [get_ports { vaux13_p }]; #IO_L5P_T0_AD13P_35 Sch=ad_p[13] ChipKit pin=A8
|
||||
#set_property -dict { PACKAGE_PIN E5 IOSTANDARD LVCMOS33 } [get_ports { vaux13_n }]; #IO_L5N_T0_AD13N_35 Sch=ad_n[13] ChipKit pin=A9
|
||||
#set_property -dict { PACKAGE_PIN A4 IOSTANDARD LVCMOS33 } [get_ports { vaux14_p }]; #IO_L8P_T1_AD14P_35 Sch=ad_p[14] ChipKit pin=A10
|
||||
#set_property -dict { PACKAGE_PIN A3 IOSTANDARD LVCMOS33 } [get_ports { vaux14_n }]; #IO_L8N_T1_AD14N_35 Sch=ad_n[14] ChipKit pin=A11
|
||||
## ChipKit Inner Analog Header - as Digital I/O
|
||||
## NOTE: the following constraints should be used when using the inner analog header ports as digital I/O.
|
||||
#set_property -dict { PACKAGE_PIN B7 IOSTANDARD LVCMOS33 } [get_ports { ck_io20 }]; #IO_L2P_T0_AD12P_35 Sch=ad_p[12] ChipKit pin=A6
|
||||
#set_property -dict { PACKAGE_PIN B6 IOSTANDARD LVCMOS33 } [get_ports { ck_io21 }]; #IO_L2N_T0_AD12N_35 Sch=ad_n[12] ChipKit pin=A7
|
||||
#set_property -dict { PACKAGE_PIN E6 IOSTANDARD LVCMOS33 } [get_ports { ck_io22 }]; #IO_L5P_T0_AD13P_35 Sch=ad_p[13] ChipKit pin=A8
|
||||
#set_property -dict { PACKAGE_PIN E5 IOSTANDARD LVCMOS33 } [get_ports { ck_io23 }]; #IO_L5N_T0_AD13N_35 Sch=ad_n[13] ChipKit pin=A9
|
||||
#set_property -dict { PACKAGE_PIN A4 IOSTANDARD LVCMOS33 } [get_ports { ck_io24 }]; #IO_L8P_T1_AD14P_35 Sch=ad_p[14] ChipKit pin=A10
|
||||
#set_property -dict { PACKAGE_PIN A3 IOSTANDARD LVCMOS33 } [get_ports { ck_io25 }]; #IO_L8N_T1_AD14N_35 Sch=ad_n[14] ChipKit pin=A11
|
||||
|
||||
## ChipKit SPI
|
||||
#set_property -dict { PACKAGE_PIN G1 IOSTANDARD LVCMOS33 } [get_ports { ck_miso }]; #IO_L17N_T2_35 Sch=ck_miso
|
||||
#set_property -dict { PACKAGE_PIN H1 IOSTANDARD LVCMOS33 } [get_ports { ck_mosi }]; #IO_L17P_T2_35 Sch=ck_mosi
|
||||
#set_property -dict { PACKAGE_PIN F1 IOSTANDARD LVCMOS33 } [get_ports { ck_sck }]; #IO_L18P_T2_35 Sch=ck_sck
|
||||
#set_property -dict { PACKAGE_PIN C1 IOSTANDARD LVCMOS33 } [get_ports { ck_ss }]; #IO_L16N_T2_35 Sch=ck_ss
|
||||
|
||||
## ChipKit I2C
|
||||
#set_property -dict { PACKAGE_PIN L18 IOSTANDARD LVCMOS33 } [get_ports { ck_scl }]; #IO_L4P_T0_D04_14 Sch=ck_scl
|
||||
#set_property -dict { PACKAGE_PIN M18 IOSTANDARD LVCMOS33 } [get_ports { ck_sda }]; #IO_L4N_T0_D05_14 Sch=ck_sda
|
||||
#set_property -dict { PACKAGE_PIN A14 IOSTANDARD LVCMOS33 } [get_ports { scl_pup }]; #IO_L9N_T1_DQS_AD3N_15 Sch=scl_pup
|
||||
#set_property -dict { PACKAGE_PIN A13 IOSTANDARD LVCMOS33 } [get_ports { sda_pup }]; #IO_L9P_T1_DQS_AD3P_15 Sch=sda_pup
|
||||
|
||||
## Misc. ChipKit Ports
|
||||
#set_property -dict { PACKAGE_PIN M17 IOSTANDARD LVCMOS33 } [get_ports { ck_ioa }]; #IO_L10N_T1_D15_14 Sch=ck_ioa
|
||||
set_property -dict {PACKAGE_PIN C2 IOSTANDARD LVCMOS33} [get_ports rst]
|
||||
|
||||
## SMSC Ethernet PHY
|
||||
#set_property -dict { PACKAGE_PIN D17 IOSTANDARD LVCMOS33 } [get_ports { eth_col }]; #IO_L16N_T2_A27_15 Sch=eth_col
|
||||
#set_property -dict { PACKAGE_PIN G14 IOSTANDARD LVCMOS33 } [get_ports { eth_crs }]; #IO_L15N_T2_DQS_ADV_B_15 Sch=eth_crs
|
||||
#set_property -dict { PACKAGE_PIN F16 IOSTANDARD LVCMOS33 } [get_ports { eth_mdc }]; #IO_L14N_T2_SRCC_15 Sch=eth_mdc
|
||||
#set_property -dict { PACKAGE_PIN K13 IOSTANDARD LVCMOS33 } [get_ports { eth_mdio }]; #IO_L17P_T2_A26_15 Sch=eth_mdio
|
||||
#set_property -dict { PACKAGE_PIN G18 IOSTANDARD LVCMOS33 } [get_ports { eth_ref_clk }]; #IO_L22P_T3_A17_15 Sch=eth_ref_clk
|
||||
#set_property -dict { PACKAGE_PIN C16 IOSTANDARD LVCMOS33 } [get_ports { eth_rstn }]; #IO_L20P_T3_A20_15 Sch=eth_rstn
|
||||
#set_property -dict { PACKAGE_PIN F15 IOSTANDARD LVCMOS33 } [get_ports { eth_rx_clk }]; #IO_L14P_T2_SRCC_15 Sch=eth_rx_clk
|
||||
#set_property -dict { PACKAGE_PIN G16 IOSTANDARD LVCMOS33 } [get_ports { eth_rx_dv }]; #IO_L13N_T2_MRCC_15 Sch=eth_rx_dv
|
||||
#set_property -dict { PACKAGE_PIN D18 IOSTANDARD LVCMOS33 } [get_ports { eth_rxd[0] }]; #IO_L21N_T3_DQS_A18_15 Sch=eth_rxd[0]
|
||||
#set_property -dict { PACKAGE_PIN E17 IOSTANDARD LVCMOS33 } [get_ports { eth_rxd[1] }]; #IO_L16P_T2_A28_15 Sch=eth_rxd[1]
|
||||
#set_property -dict { PACKAGE_PIN E18 IOSTANDARD LVCMOS33 } [get_ports { eth_rxd[2] }]; #IO_L21P_T3_DQS_15 Sch=eth_rxd[2]
|
||||
#set_property -dict { PACKAGE_PIN G17 IOSTANDARD LVCMOS33 } [get_ports { eth_rxd[3] }]; #IO_L18N_T2_A23_15 Sch=eth_rxd[3]
|
||||
#set_property -dict { PACKAGE_PIN C17 IOSTANDARD LVCMOS33 } [get_ports { eth_rxerr }]; #IO_L20N_T3_A19_15 Sch=eth_rxerr
|
||||
#set_property -dict { PACKAGE_PIN H16 IOSTANDARD LVCMOS33 } [get_ports { eth_tx_clk }]; #IO_L13P_T2_MRCC_15 Sch=eth_tx_clk
|
||||
#set_property -dict { PACKAGE_PIN H15 IOSTANDARD LVCMOS33 } [get_ports { eth_tx_en }]; #IO_L19N_T3_A21_VREF_15 Sch=eth_tx_en
|
||||
#set_property -dict { PACKAGE_PIN H14 IOSTANDARD LVCMOS33 } [get_ports { eth_txd[0] }]; #IO_L15P_T2_DQS_15 Sch=eth_txd[0]
|
||||
#set_property -dict { PACKAGE_PIN J14 IOSTANDARD LVCMOS33 } [get_ports { eth_txd[1] }]; #IO_L19P_T3_A22_15 Sch=eth_txd[1]
|
||||
#set_property -dict { PACKAGE_PIN J13 IOSTANDARD LVCMOS33 } [get_ports { eth_txd[2] }]; #IO_L17N_T2_A25_15 Sch=eth_txd[2]
|
||||
#set_property -dict { PACKAGE_PIN H17 IOSTANDARD LVCMOS33 } [get_ports { eth_txd[3] }]; #IO_L18P_T2_A24_15 Sch=eth_txd[3]
|
||||
|
||||
## Quad SPI Flash
|
||||
#set_property -dict { PACKAGE_PIN L13 IOSTANDARD LVCMOS33 } [get_ports { qspi_cs }]; #IO_L6P_T0_FCS_B_14 Sch=qspi_cs
|
||||
#set_property -dict { PACKAGE_PIN K17 IOSTANDARD LVCMOS33 } [get_ports { qspi_dq[0] }]; #IO_L1P_T0_D00_MOSI_14 Sch=qspi_dq[0]
|
||||
#set_property -dict { PACKAGE_PIN K18 IOSTANDARD LVCMOS33 } [get_ports { qspi_dq[1] }]; #IO_L1N_T0_D01_DIN_14 Sch=qspi_dq[1]
|
||||
#set_property -dict { PACKAGE_PIN L14 IOSTANDARD LVCMOS33 } [get_ports { qspi_dq[2] }]; #IO_L2P_T0_D02_14 Sch=qspi_dq[2]
|
||||
#set_property -dict { PACKAGE_PIN M14 IOSTANDARD LVCMOS33 } [get_ports { qspi_dq[3] }]; #IO_L2N_T0_D03_14 Sch=qspi_dq[3]
|
||||
|
||||
## Power Measurements
|
||||
#set_property -dict { PACKAGE_PIN B17 IOSTANDARD LVCMOS33 } [get_ports { vsnsvu_n }]; #IO_L7N_T1_AD2N_15 Sch=ad_n[2]
|
||||
#set_property -dict { PACKAGE_PIN B16 IOSTANDARD LVCMOS33 } [get_ports { vsnsvu_p }]; #IO_L7P_T1_AD2P_15 Sch=ad_p[2]
|
||||
#set_property -dict { PACKAGE_PIN B12 IOSTANDARD LVCMOS33 } [get_ports { vsns5v0_n }]; #IO_L3N_T0_DQS_AD1N_15 Sch=ad_n[1]
|
||||
#set_property -dict { PACKAGE_PIN C12 IOSTANDARD LVCMOS33 } [get_ports { vsns5v0_p }]; #IO_L3P_T0_DQS_AD1P_15 Sch=ad_p[1]
|
||||
#set_property -dict { PACKAGE_PIN F14 IOSTANDARD LVCMOS33 } [get_ports { isns5v0_n }]; #IO_L5N_T0_AD9N_15 Sch=ad_n[9]
|
||||
#set_property -dict { PACKAGE_PIN F13 IOSTANDARD LVCMOS33 } [get_ports { isns5v0_p }]; #IO_L5P_T0_AD9P_15 Sch=ad_p[9]
|
||||
#set_property -dict { PACKAGE_PIN A16 IOSTANDARD LVCMOS33 } [get_ports { isns0v95_n }]; #IO_L8N_T1_AD10N_15 Sch=ad_n[10]
|
||||
#set_property -dict { PACKAGE_PIN A15 IOSTANDARD LVCMOS33 } [get_ports { isns0v95_p }]; #IO_L8P_T1_AD10P_15 Sch=ad_p[10]
|
||||
|
||||
set_property BITSTREAM.GENERAL.COMPRESS True [current_design]
|
||||
48
rtl/arty-a7/Arty_C_mig.ucf
Normal file
48
rtl/arty-a7/Arty_C_mig.ucf
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
NET "ddr3_dq[0]" LOC = "K5" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_dq[1]" LOC = "L3" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_dq[2]" LOC = "K3" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_dq[3]" LOC = "L6" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_dq[4]" LOC = "M3" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_dq[5]" LOC = "M1" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_dq[6]" LOC = "L4" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_dq[7]" LOC = "M2" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_dq[8]" LOC = "V4" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_dq[9]" LOC = "T5" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_dq[10]" LOC = "U4" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_dq[11]" LOC = "V5" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_dq[12]" LOC = "V1" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_dq[13]" LOC = "T3" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_dq[14]" LOC = "U3" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_dq[15]" LOC = "R3" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_dm[0]" LOC = "L1" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_dm[1]" LOC = "U1" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_dqs_p[0]" LOC = "N2" | IOSTANDARD = DIFF_SSTL135 ;
|
||||
NET "ddr3_dqs_n[0]" LOC = "N1" | IOSTANDARD = DIFF_SSTL135 ;
|
||||
NET "ddr3_dqs_p[1]" LOC = "U2" | IOSTANDARD = DIFF_SSTL135 ;
|
||||
NET "ddr3_dqs_n[1]" LOC = "V2" | IOSTANDARD = DIFF_SSTL135 ;
|
||||
NET "ddr3_addr[13]" LOC = "T8" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_addr[12]" LOC = "T6" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_addr[11]" LOC = "U6" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_addr[10]" LOC = "R6" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_addr[9]" LOC = "V7" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_addr[8]" LOC = "R8" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_addr[7]" LOC = "U7" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_addr[6]" LOC = "V6" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_addr[5]" LOC = "R7" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_addr[4]" LOC = "N6" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_addr[3]" LOC = "T1" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_addr[2]" LOC = "N4" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_addr[1]" LOC = "M6" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_addr[0]" LOC = "R2" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_ba[2]" LOC = "P2" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_ba[1]" LOC = "P4" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_ba[0]" LOC = "R1" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_ck_p[0]" LOC = "U9" | IOSTANDARD = DIFF_SSTL135 ;
|
||||
NET "ddr3_ck_n[0]" LOC = "V9" | IOSTANDARD = DIFF_SSTL135 ;
|
||||
NET "ddr3_ras_n" LOC = "P3" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_cas_n" LOC = "M4" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_we_n" LOC = "P5" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_reset_n" LOC = "K6" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_cke[0]" LOC = "N5" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_odt[0]" LOC = "R5" | IOSTANDARD = SSTL135 ;
|
||||
NET "ddr3_cs_n[0]" LOC = "U8" | IOSTANDARD = SSTL135 ;
|
||||
148
rtl/arty-a7/mig_dram_0/mig_a.prj
Normal file
148
rtl/arty-a7/mig_dram_0/mig_a.prj
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
|
||||
<Project NoOfControllers="1">
|
||||
|
||||
|
||||
|
||||
<!-- IMPORTANT: This is an internal file that has been generated by the MIG software. Any direct editing or changes made to this file may result in unpredictable behavior or data corruption. It is strongly advised that users do not edit the contents of this file. Re-run the MIG GUI with the required settings if any of the options provided below need to be altered. -->
|
||||
|
||||
<ModuleName>mig_dram_0</ModuleName>
|
||||
|
||||
<dci_inouts_inputs>1</dci_inouts_inputs>
|
||||
|
||||
<dci_inputs>1</dci_inputs>
|
||||
|
||||
<Debug_En>OFF</Debug_En>
|
||||
|
||||
<DataDepth_En>1024</DataDepth_En>
|
||||
|
||||
<LowPower_En>ON</LowPower_En>
|
||||
|
||||
<XADC_En>Enabled</XADC_En>
|
||||
|
||||
<TargetFPGA>xc7a35ti-csg324/-1L</TargetFPGA>
|
||||
|
||||
<Version>4.2</Version>
|
||||
|
||||
<SystemClock>No Buffer</SystemClock>
|
||||
|
||||
<ReferenceClock>No Buffer</ReferenceClock>
|
||||
|
||||
<SysResetPolarity>ACTIVE LOW</SysResetPolarity>
|
||||
|
||||
<BankSelectionFlag>FALSE</BankSelectionFlag>
|
||||
|
||||
<InternalVref>1</InternalVref>
|
||||
|
||||
<dci_hr_inouts_inputs>50 Ohms</dci_hr_inouts_inputs>
|
||||
|
||||
<dci_cascade>0</dci_cascade>
|
||||
|
||||
<Controller number="0">
|
||||
<MemoryDevice>DDR3_SDRAM/Components/MT41K128M16XX-15E</MemoryDevice>
|
||||
<TimePeriod>3000</TimePeriod>
|
||||
<VccAuxIO>1.8V</VccAuxIO>
|
||||
<PHYRatio>4:1</PHYRatio>
|
||||
<InputClkFreq>83.333</InputClkFreq>
|
||||
<UIExtraClocks>0</UIExtraClocks>
|
||||
<MMCM_VCO>666</MMCM_VCO>
|
||||
<MMCMClkOut0> 1.000</MMCMClkOut0>
|
||||
<MMCMClkOut1>1</MMCMClkOut1>
|
||||
<MMCMClkOut2>1</MMCMClkOut2>
|
||||
<MMCMClkOut3>1</MMCMClkOut3>
|
||||
<MMCMClkOut4>1</MMCMClkOut4>
|
||||
<DataWidth>16</DataWidth>
|
||||
<DeepMemory>1</DeepMemory>
|
||||
<DataMask>1</DataMask>
|
||||
<ECC>Disabled</ECC>
|
||||
<Ordering>Strict</Ordering>
|
||||
<BankMachineCnt>4</BankMachineCnt>
|
||||
<CustomPart>FALSE</CustomPart>
|
||||
<NewPartName/>
|
||||
<RowAddress>14</RowAddress>
|
||||
<ColAddress>10</ColAddress>
|
||||
<BankAddress>3</BankAddress>
|
||||
<MemoryVoltage>1.35V</MemoryVoltage>
|
||||
<UserMemoryAddressMap>BANK_ROW_COLUMN</UserMemoryAddressMap>
|
||||
<PinSelection>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="R2" SLEW="" VCCAUX_IO="" name="ddr3_addr[0]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="R6" SLEW="" VCCAUX_IO="" name="ddr3_addr[10]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="U6" SLEW="" VCCAUX_IO="" name="ddr3_addr[11]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="T6" SLEW="" VCCAUX_IO="" name="ddr3_addr[12]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="T8" SLEW="" VCCAUX_IO="" name="ddr3_addr[13]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="M6" SLEW="" VCCAUX_IO="" name="ddr3_addr[1]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="N4" SLEW="" VCCAUX_IO="" name="ddr3_addr[2]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="T1" SLEW="" VCCAUX_IO="" name="ddr3_addr[3]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="N6" SLEW="" VCCAUX_IO="" name="ddr3_addr[4]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="R7" SLEW="" VCCAUX_IO="" name="ddr3_addr[5]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="V6" SLEW="" VCCAUX_IO="" name="ddr3_addr[6]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="U7" SLEW="" VCCAUX_IO="" name="ddr3_addr[7]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="R8" SLEW="" VCCAUX_IO="" name="ddr3_addr[8]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="V7" SLEW="" VCCAUX_IO="" name="ddr3_addr[9]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="R1" SLEW="" VCCAUX_IO="" name="ddr3_ba[0]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="P4" SLEW="" VCCAUX_IO="" name="ddr3_ba[1]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="P2" SLEW="" VCCAUX_IO="" name="ddr3_ba[2]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="M4" SLEW="" VCCAUX_IO="" name="ddr3_cas_n"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="DIFF_SSTL135" PADName="V9" SLEW="" VCCAUX_IO="" name="ddr3_ck_n[0]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="DIFF_SSTL135" PADName="U9" SLEW="" VCCAUX_IO="" name="ddr3_ck_p[0]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="N5" SLEW="" VCCAUX_IO="" name="ddr3_cke[0]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="U8" SLEW="" VCCAUX_IO="" name="ddr3_cs_n[0]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="L1" SLEW="" VCCAUX_IO="" name="ddr3_dm[0]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="U1" SLEW="" VCCAUX_IO="" name="ddr3_dm[1]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="K5" SLEW="" VCCAUX_IO="" name="ddr3_dq[0]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="U4" SLEW="" VCCAUX_IO="" name="ddr3_dq[10]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="V5" SLEW="" VCCAUX_IO="" name="ddr3_dq[11]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="V1" SLEW="" VCCAUX_IO="" name="ddr3_dq[12]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="T3" SLEW="" VCCAUX_IO="" name="ddr3_dq[13]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="U3" SLEW="" VCCAUX_IO="" name="ddr3_dq[14]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="R3" SLEW="" VCCAUX_IO="" name="ddr3_dq[15]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="L3" SLEW="" VCCAUX_IO="" name="ddr3_dq[1]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="K3" SLEW="" VCCAUX_IO="" name="ddr3_dq[2]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="L6" SLEW="" VCCAUX_IO="" name="ddr3_dq[3]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="M3" SLEW="" VCCAUX_IO="" name="ddr3_dq[4]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="M1" SLEW="" VCCAUX_IO="" name="ddr3_dq[5]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="L4" SLEW="" VCCAUX_IO="" name="ddr3_dq[6]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="M2" SLEW="" VCCAUX_IO="" name="ddr3_dq[7]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="V4" SLEW="" VCCAUX_IO="" name="ddr3_dq[8]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="T5" SLEW="" VCCAUX_IO="" name="ddr3_dq[9]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="DIFF_SSTL135" PADName="N1" SLEW="" VCCAUX_IO="" name="ddr3_dqs_n[0]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="DIFF_SSTL135" PADName="V2" SLEW="" VCCAUX_IO="" name="ddr3_dqs_n[1]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="DIFF_SSTL135" PADName="N2" SLEW="" VCCAUX_IO="" name="ddr3_dqs_p[0]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="DIFF_SSTL135" PADName="U2" SLEW="" VCCAUX_IO="" name="ddr3_dqs_p[1]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="R5" SLEW="" VCCAUX_IO="" name="ddr3_odt[0]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="P3" SLEW="" VCCAUX_IO="" name="ddr3_ras_n"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="K6" SLEW="" VCCAUX_IO="" name="ddr3_reset_n"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="P5" SLEW="" VCCAUX_IO="" name="ddr3_we_n"/>
|
||||
</PinSelection>
|
||||
<System_Control>
|
||||
<Pin Bank="Select Bank" PADName="No connect" name="sys_rst"/>
|
||||
<Pin Bank="Select Bank" PADName="No connect" name="init_calib_complete"/>
|
||||
<Pin Bank="Select Bank" PADName="No connect" name="tg_compare_error"/>
|
||||
</System_Control>
|
||||
<TimingParameters>
|
||||
<Parameters tcke="5.625" tfaw="45" tras="36" trcd="13.5" trefi="7.8" trfc="160" trp="13.5" trrd="7.5" trtp="7.5" twtr="7.5"/>
|
||||
</TimingParameters>
|
||||
<mrBurstLength name="Burst Length">8 - Fixed</mrBurstLength>
|
||||
<mrBurstType name="Read Burst Type and Length">Sequential</mrBurstType>
|
||||
<mrCasLatency name="CAS Latency">5</mrCasLatency>
|
||||
<mrMode name="Mode">Normal</mrMode>
|
||||
<mrDllReset name="DLL Reset">No</mrDllReset>
|
||||
<mrPdMode name="DLL control for precharge PD">Slow Exit</mrPdMode>
|
||||
<emrDllEnable name="DLL Enable">Enable</emrDllEnable>
|
||||
<emrOutputDriveStrength name="Output Driver Impedance Control">RZQ/6</emrOutputDriveStrength>
|
||||
<emrMirrorSelection name="Address Mirroring">Disable</emrMirrorSelection>
|
||||
<emrCSSelection name="Controller Chip Select Pin">Enable</emrCSSelection>
|
||||
<emrRTT name="RTT (nominal) - On Die Termination (ODT)">RZQ/6</emrRTT>
|
||||
<emrPosted name="Additive Latency (AL)">0</emrPosted>
|
||||
<emrOCD name="Write Leveling Enable">Disabled</emrOCD>
|
||||
<emrDQS name="TDQS enable">Enabled</emrDQS>
|
||||
<emrRDQS name="Qoff">Output Buffer Enabled</emrRDQS>
|
||||
<mr2PartialArraySelfRefresh name="Partial-Array Self Refresh">Full Array</mr2PartialArraySelfRefresh>
|
||||
<mr2CasWriteLatency name="CAS write latency">5</mr2CasWriteLatency>
|
||||
<mr2AutoSelfRefresh name="Auto Self Refresh">Enabled</mr2AutoSelfRefresh>
|
||||
<mr2SelfRefreshTempRange name="High Temparature Self Refresh Rate">Normal</mr2SelfRefreshTempRange>
|
||||
<mr2RTTWR name="RTT_WR - Dynamic On Die Termination (ODT)">Dynamic ODT off</mr2RTTWR>
|
||||
<PortInterface>NATIVE</PortInterface>
|
||||
</Controller>
|
||||
|
||||
|
||||
</Project>
|
||||
148
rtl/arty-a7/mig_dram_0/mig_b.prj
Normal file
148
rtl/arty-a7/mig_dram_0/mig_b.prj
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
|
||||
<Project NoOfControllers="1">
|
||||
|
||||
|
||||
|
||||
<!-- IMPORTANT: This is an internal file that has been generated by the MIG software. Any direct editing or changes made to this file may result in unpredictable behavior or data corruption. It is strongly advised that users do not edit the contents of this file. Re-run the MIG GUI with the required settings if any of the options provided below need to be altered. -->
|
||||
|
||||
<ModuleName>mig_dram_0</ModuleName>
|
||||
|
||||
<dci_inouts_inputs>1</dci_inouts_inputs>
|
||||
|
||||
<dci_inputs>1</dci_inputs>
|
||||
|
||||
<Debug_En>OFF</Debug_En>
|
||||
|
||||
<DataDepth_En>1024</DataDepth_En>
|
||||
|
||||
<LowPower_En>ON</LowPower_En>
|
||||
|
||||
<XADC_En>Enabled</XADC_En>
|
||||
|
||||
<TargetFPGA>xc7a35ti-csg324/-1L</TargetFPGA>
|
||||
|
||||
<Version>4.2</Version>
|
||||
|
||||
<SystemClock>No Buffer</SystemClock>
|
||||
|
||||
<ReferenceClock>No Buffer</ReferenceClock>
|
||||
|
||||
<SysResetPolarity>ACTIVE LOW</SysResetPolarity>
|
||||
|
||||
<BankSelectionFlag>FALSE</BankSelectionFlag>
|
||||
|
||||
<InternalVref>1</InternalVref>
|
||||
|
||||
<dci_hr_inouts_inputs>50 Ohms</dci_hr_inouts_inputs>
|
||||
|
||||
<dci_cascade>0</dci_cascade>
|
||||
|
||||
<Controller number="0">
|
||||
<MemoryDevice>DDR3_SDRAM/Components/MT41K128M16XX-15E</MemoryDevice>
|
||||
<TimePeriod>3000</TimePeriod>
|
||||
<VccAuxIO>1.8V</VccAuxIO>
|
||||
<PHYRatio>4:1</PHYRatio>
|
||||
<InputClkFreq>83.333</InputClkFreq>
|
||||
<UIExtraClocks>0</UIExtraClocks>
|
||||
<MMCM_VCO>666</MMCM_VCO>
|
||||
<MMCMClkOut0> 1.000</MMCMClkOut0>
|
||||
<MMCMClkOut1>1</MMCMClkOut1>
|
||||
<MMCMClkOut2>1</MMCMClkOut2>
|
||||
<MMCMClkOut3>1</MMCMClkOut3>
|
||||
<MMCMClkOut4>1</MMCMClkOut4>
|
||||
<DataWidth>16</DataWidth>
|
||||
<DeepMemory>1</DeepMemory>
|
||||
<DataMask>1</DataMask>
|
||||
<ECC>Disabled</ECC>
|
||||
<Ordering>Strict</Ordering>
|
||||
<BankMachineCnt>4</BankMachineCnt>
|
||||
<CustomPart>FALSE</CustomPart>
|
||||
<NewPartName/>
|
||||
<RowAddress>14</RowAddress>
|
||||
<ColAddress>10</ColAddress>
|
||||
<BankAddress>3</BankAddress>
|
||||
<MemoryVoltage>1.35V</MemoryVoltage>
|
||||
<UserMemoryAddressMap>BANK_ROW_COLUMN</UserMemoryAddressMap>
|
||||
<PinSelection>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="R2" SLEW="" VCCAUX_IO="" name="ddr3_addr[0]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="R6" SLEW="" VCCAUX_IO="" name="ddr3_addr[10]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="U6" SLEW="" VCCAUX_IO="" name="ddr3_addr[11]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="T6" SLEW="" VCCAUX_IO="" name="ddr3_addr[12]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="T8" SLEW="" VCCAUX_IO="" name="ddr3_addr[13]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="M6" SLEW="" VCCAUX_IO="" name="ddr3_addr[1]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="N4" SLEW="" VCCAUX_IO="" name="ddr3_addr[2]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="T1" SLEW="" VCCAUX_IO="" name="ddr3_addr[3]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="N6" SLEW="" VCCAUX_IO="" name="ddr3_addr[4]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="R7" SLEW="" VCCAUX_IO="" name="ddr3_addr[5]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="V6" SLEW="" VCCAUX_IO="" name="ddr3_addr[6]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="U7" SLEW="" VCCAUX_IO="" name="ddr3_addr[7]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="R8" SLEW="" VCCAUX_IO="" name="ddr3_addr[8]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="V7" SLEW="" VCCAUX_IO="" name="ddr3_addr[9]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="R1" SLEW="" VCCAUX_IO="" name="ddr3_ba[0]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="P4" SLEW="" VCCAUX_IO="" name="ddr3_ba[1]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="P2" SLEW="" VCCAUX_IO="" name="ddr3_ba[2]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="M4" SLEW="" VCCAUX_IO="" name="ddr3_cas_n"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="DIFF_SSTL135" PADName="V9" SLEW="" VCCAUX_IO="" name="ddr3_ck_n[0]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="DIFF_SSTL135" PADName="U9" SLEW="" VCCAUX_IO="" name="ddr3_ck_p[0]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="N5" SLEW="" VCCAUX_IO="" name="ddr3_cke[0]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="U8" SLEW="" VCCAUX_IO="" name="ddr3_cs_n[0]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="L1" SLEW="" VCCAUX_IO="" name="ddr3_dm[0]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="U1" SLEW="" VCCAUX_IO="" name="ddr3_dm[1]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="K5" SLEW="" VCCAUX_IO="" name="ddr3_dq[0]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="U4" SLEW="" VCCAUX_IO="" name="ddr3_dq[10]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="V5" SLEW="" VCCAUX_IO="" name="ddr3_dq[11]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="V1" SLEW="" VCCAUX_IO="" name="ddr3_dq[12]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="T3" SLEW="" VCCAUX_IO="" name="ddr3_dq[13]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="U3" SLEW="" VCCAUX_IO="" name="ddr3_dq[14]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="R3" SLEW="" VCCAUX_IO="" name="ddr3_dq[15]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="L3" SLEW="" VCCAUX_IO="" name="ddr3_dq[1]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="K3" SLEW="" VCCAUX_IO="" name="ddr3_dq[2]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="L6" SLEW="" VCCAUX_IO="" name="ddr3_dq[3]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="M3" SLEW="" VCCAUX_IO="" name="ddr3_dq[4]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="M1" SLEW="" VCCAUX_IO="" name="ddr3_dq[5]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="L4" SLEW="" VCCAUX_IO="" name="ddr3_dq[6]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="M2" SLEW="" VCCAUX_IO="" name="ddr3_dq[7]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="V4" SLEW="" VCCAUX_IO="" name="ddr3_dq[8]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="T5" SLEW="" VCCAUX_IO="" name="ddr3_dq[9]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="DIFF_SSTL135" PADName="N1" SLEW="" VCCAUX_IO="" name="ddr3_dqs_n[0]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="DIFF_SSTL135" PADName="V2" SLEW="" VCCAUX_IO="" name="ddr3_dqs_n[1]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="DIFF_SSTL135" PADName="N2" SLEW="" VCCAUX_IO="" name="ddr3_dqs_p[0]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="DIFF_SSTL135" PADName="U2" SLEW="" VCCAUX_IO="" name="ddr3_dqs_p[1]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="R5" SLEW="" VCCAUX_IO="" name="ddr3_odt[0]"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="P3" SLEW="" VCCAUX_IO="" name="ddr3_ras_n"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="K6" SLEW="" VCCAUX_IO="" name="ddr3_reset_n"/>
|
||||
<Pin IN_TERM="" IOSTANDARD="SSTL135" PADName="P5" SLEW="" VCCAUX_IO="" name="ddr3_we_n"/>
|
||||
</PinSelection>
|
||||
<System_Control>
|
||||
<Pin Bank="Select Bank" PADName="No connect" name="sys_rst"/>
|
||||
<Pin Bank="Select Bank" PADName="No connect" name="init_calib_complete"/>
|
||||
<Pin Bank="Select Bank" PADName="No connect" name="tg_compare_error"/>
|
||||
</System_Control>
|
||||
<TimingParameters>
|
||||
<Parameters tcke="5.625" tfaw="45" tras="36" trcd="13.5" trefi="7.8" trfc="160" trp="13.5" trrd="7.5" trtp="7.5" twtr="7.5"/>
|
||||
</TimingParameters>
|
||||
<mrBurstLength name="Burst Length">8 - Fixed</mrBurstLength>
|
||||
<mrBurstType name="Read Burst Type and Length">Sequential</mrBurstType>
|
||||
<mrCasLatency name="CAS Latency">5</mrCasLatency>
|
||||
<mrMode name="Mode">Normal</mrMode>
|
||||
<mrDllReset name="DLL Reset">No</mrDllReset>
|
||||
<mrPdMode name="DLL control for precharge PD">Slow Exit</mrPdMode>
|
||||
<emrDllEnable name="DLL Enable">Enable</emrDllEnable>
|
||||
<emrOutputDriveStrength name="Output Driver Impedance Control">RZQ/6</emrOutputDriveStrength>
|
||||
<emrMirrorSelection name="Address Mirroring">Disable</emrMirrorSelection>
|
||||
<emrCSSelection name="Controller Chip Select Pin">Enable</emrCSSelection>
|
||||
<emrRTT name="RTT (nominal) - On Die Termination (ODT)">RZQ/6</emrRTT>
|
||||
<emrPosted name="Additive Latency (AL)">0</emrPosted>
|
||||
<emrOCD name="Write Leveling Enable">Disabled</emrOCD>
|
||||
<emrDQS name="TDQS enable">Enabled</emrDQS>
|
||||
<emrRDQS name="Qoff">Output Buffer Enabled</emrRDQS>
|
||||
<mr2PartialArraySelfRefresh name="Partial-Array Self Refresh">Full Array</mr2PartialArraySelfRefresh>
|
||||
<mr2CasWriteLatency name="CAS write latency">5</mr2CasWriteLatency>
|
||||
<mr2AutoSelfRefresh name="Auto Self Refresh">Enabled</mr2AutoSelfRefresh>
|
||||
<mr2SelfRefreshTempRange name="High Temparature Self Refresh Rate">Normal</mr2SelfRefreshTempRange>
|
||||
<mr2RTTWR name="RTT_WR - Dynamic On Die Termination (ODT)">Dynamic ODT off</mr2RTTWR>
|
||||
<PortInterface>NATIVE</PortInterface>
|
||||
</Controller>
|
||||
|
||||
|
||||
</Project>
|
||||
176
rtl/arty-a7/sdspi_testbench_behav.wcfg
Normal file
176
rtl/arty-a7/sdspi_testbench_behav.wcfg
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<wave_config>
|
||||
<wave_state>
|
||||
</wave_state>
|
||||
<db_ref_list>
|
||||
<db_ref path="sdspi_testbench_behav.wdb" id="1">
|
||||
<top_modules>
|
||||
<top_module name="glbl" />
|
||||
<top_module name="sdspi_testbench" />
|
||||
</top_modules>
|
||||
</db_ref>
|
||||
</db_ref_list>
|
||||
<zoom_setting>
|
||||
<ZoomStartTime time="35023635fs"></ZoomStartTime>
|
||||
<ZoomEndTime time="36066046fs"></ZoomEndTime>
|
||||
<Cursor1Time time="32155000fs"></Cursor1Time>
|
||||
</zoom_setting>
|
||||
<column_width_setting>
|
||||
<NameColumnWidth column_width="149"></NameColumnWidth>
|
||||
<ValueColumnWidth column_width="90"></ValueColumnWidth>
|
||||
</column_width_setting>
|
||||
<WVObjectSize size="35" />
|
||||
<wave_markers>
|
||||
<marker time="30925000" label="" />
|
||||
</wave_markers>
|
||||
<wvobject fp_name="/sdspi_testbench/clk" type="logic">
|
||||
<obj_property name="ElementShortName">clk</obj_property>
|
||||
<obj_property name="ObjectShortName">clk</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/sdspi_testbench/tx_data" type="array">
|
||||
<obj_property name="ElementShortName">tx_data[7:0]</obj_property>
|
||||
<obj_property name="ObjectShortName">tx_data[7:0]</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/sdspi_testbench/rx_data" type="array">
|
||||
<obj_property name="ElementShortName">rx_data[7:0]</obj_property>
|
||||
<obj_property name="ObjectShortName">rx_data[7:0]</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/sdspi_testbench/UUT/rx_shifter" type="array">
|
||||
<obj_property name="ElementShortName">rx_shifter[7:0]</obj_property>
|
||||
<obj_property name="ObjectShortName">rx_shifter[7:0]</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/sdspi_testbench/UUT/tx_shifter" type="array">
|
||||
<obj_property name="ElementShortName">tx_shifter[7:0]</obj_property>
|
||||
<obj_property name="ObjectShortName">tx_shifter[7:0]</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/sdspi_testbench/UUT/tx_fifo_out" type="array">
|
||||
<obj_property name="ElementShortName">tx_fifo_out[7:0]</obj_property>
|
||||
<obj_property name="ObjectShortName">tx_fifo_out[7:0]</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/sdspi_testbench/UUT/tx_fifo_rd_en" type="logic">
|
||||
<obj_property name="ElementShortName">tx_fifo_rd_en</obj_property>
|
||||
<obj_property name="ObjectShortName">tx_fifo_rd_en</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/sdspi_testbench/UUT/tx_fifo_wr_en" type="logic">
|
||||
<obj_property name="ElementShortName">tx_fifo_wr_en</obj_property>
|
||||
<obj_property name="ObjectShortName">tx_fifo_wr_en</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/sdspi_testbench/UUT/rx_fifo_wr_en" type="logic">
|
||||
<obj_property name="ElementShortName">rx_fifo_wr_en</obj_property>
|
||||
<obj_property name="ObjectShortName">rx_fifo_wr_en</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/sdspi_testbench/UUT/rx_bit_recvd" type="logic">
|
||||
<obj_property name="ElementShortName">rx_bit_recvd</obj_property>
|
||||
<obj_property name="ObjectShortName">rx_bit_recvd</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/sdspi_testbench/UUT/tx_fifo/tail_x" type="array">
|
||||
<obj_property name="ElementShortName">tail_x[4:0]</obj_property>
|
||||
<obj_property name="ObjectShortName">tail_x[4:0]</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/sdspi_testbench/UUT/tx_fifo/head_x" type="array">
|
||||
<obj_property name="ElementShortName">head_x[4:0]</obj_property>
|
||||
<obj_property name="ObjectShortName">head_x[4:0]</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/sdspi_testbench/tx_ready" type="logic">
|
||||
<obj_property name="ElementShortName">tx_ready</obj_property>
|
||||
<obj_property name="ObjectShortName">tx_ready</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/sdspi_testbench/tx_empty" type="logic">
|
||||
<obj_property name="ElementShortName">tx_empty</obj_property>
|
||||
<obj_property name="ObjectShortName">tx_empty</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/sdspi_testbench/rx_avail" type="logic">
|
||||
<obj_property name="ElementShortName">rx_avail</obj_property>
|
||||
<obj_property name="ObjectShortName">rx_avail</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/sdspi_testbench/tx_write" type="logic">
|
||||
<obj_property name="ElementShortName">tx_write</obj_property>
|
||||
<obj_property name="ObjectShortName">tx_write</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/sdspi_testbench/rx_read" type="logic">
|
||||
<obj_property name="ElementShortName">rx_read</obj_property>
|
||||
<obj_property name="ObjectShortName">rx_read</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/sdspi_testbench/ctrl_write" type="logic">
|
||||
<obj_property name="ElementShortName">ctrl_write</obj_property>
|
||||
<obj_property name="ObjectShortName">ctrl_write</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/sdspi_testbench/rx_filter_en" type="logic">
|
||||
<obj_property name="ElementShortName">rx_filter_en</obj_property>
|
||||
<obj_property name="ObjectShortName">rx_filter_en</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/sdspi_testbench/txrx_en" type="logic">
|
||||
<obj_property name="ElementShortName">txrx_en</obj_property>
|
||||
<obj_property name="ObjectShortName">txrx_en</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/sdspi_testbench/spiclk_div_wr" type="logic">
|
||||
<obj_property name="ElementShortName">spiclk_div_wr</obj_property>
|
||||
<obj_property name="ObjectShortName">spiclk_div_wr</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/sdspi_testbench/UUT/spi_clk_on" type="logic">
|
||||
<obj_property name="ElementShortName">spi_clk_on</obj_property>
|
||||
<obj_property name="ObjectShortName">spi_clk_on</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/sdspi_testbench/spiclk_f_en" type="logic">
|
||||
<obj_property name="ElementShortName">spiclk_f_en</obj_property>
|
||||
<obj_property name="ObjectShortName">spiclk_f_en</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/sdspi_testbench/UUT/spi_clk_f_on" type="logic">
|
||||
<obj_property name="ElementShortName">spi_clk_f_on</obj_property>
|
||||
<obj_property name="ObjectShortName">spi_clk_f_on</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/sdspi_testbench/sd_cs_n" type="logic">
|
||||
<obj_property name="ElementShortName">sd_cs_n</obj_property>
|
||||
<obj_property name="ObjectShortName">sd_cs_n</obj_property>
|
||||
<obj_property name="CustomSignalColor">#DCDCDC</obj_property>
|
||||
<obj_property name="UseCustomSignalColor">true</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/sdspi_testbench/sd_mosi" type="logic">
|
||||
<obj_property name="ElementShortName">sd_mosi</obj_property>
|
||||
<obj_property name="ObjectShortName">sd_mosi</obj_property>
|
||||
<obj_property name="CustomSignalColor">#00FFFF</obj_property>
|
||||
<obj_property name="UseCustomSignalColor">true</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/sdspi_testbench/sd_miso" type="logic">
|
||||
<obj_property name="ElementShortName">sd_miso</obj_property>
|
||||
<obj_property name="ObjectShortName">sd_miso</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/sdspi_testbench/sd_sck" type="logic">
|
||||
<obj_property name="ElementShortName">sd_sck</obj_property>
|
||||
<obj_property name="ObjectShortName">sd_sck</obj_property>
|
||||
<obj_property name="CustomSignalColor">#FFFF00</obj_property>
|
||||
<obj_property name="UseCustomSignalColor">true</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/sdspi_testbench/UUT/xcvr_on" type="logic">
|
||||
<obj_property name="ElementShortName">xcvr_on</obj_property>
|
||||
<obj_property name="ObjectShortName">xcvr_on</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/sdspi_testbench/UUT/hphase_start" type="logic">
|
||||
<obj_property name="ElementShortName">hphase_start</obj_property>
|
||||
<obj_property name="ObjectShortName">hphase_start</obj_property>
|
||||
<obj_property name="CustomSignalColor">#F0E68C</obj_property>
|
||||
<obj_property name="UseCustomSignalColor">true</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/sdspi_testbench/UUT/clk_phase" type="array">
|
||||
<obj_property name="ElementShortName">clk_phase[1:0]</obj_property>
|
||||
<obj_property name="ObjectShortName">clk_phase[1:0]</obj_property>
|
||||
<obj_property name="CustomSignalColor">#D2691E</obj_property>
|
||||
<obj_property name="UseCustomSignalColor">true</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/sdspi_testbench/UUT/running" type="logic">
|
||||
<obj_property name="ElementShortName">running</obj_property>
|
||||
<obj_property name="ObjectShortName">running</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/sdspi_testbench/UUT/xcvr_bitcount" type="array">
|
||||
<obj_property name="ElementShortName">xcvr_bitcount[3:0]</obj_property>
|
||||
<obj_property name="ObjectShortName">xcvr_bitcount[3:0]</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/sdspi_testbench/UUT/spi_clk_count" type="array">
|
||||
<obj_property name="ElementShortName">spi_clk_count[6:0]</obj_property>
|
||||
<obj_property name="ObjectShortName">spi_clk_count[6:0]</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/sdspi_testbench/UUT/spi_clk_div" type="array">
|
||||
<obj_property name="ElementShortName">spi_clk_div[6:0]</obj_property>
|
||||
<obj_property name="ObjectShortName">spi_clk_div[6:0]</obj_property>
|
||||
</wvobject>
|
||||
</wave_config>
|
||||
88
rtl/arty-a7/testbench_behav1.wcfg
Normal file
88
rtl/arty-a7/testbench_behav1.wcfg
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<wave_config>
|
||||
<wave_state>
|
||||
</wave_state>
|
||||
<db_ref_list>
|
||||
<db_ref path="testbench_behav.wdb" id="1">
|
||||
<top_modules>
|
||||
<top_module name="glbl" />
|
||||
<top_module name="testbench" />
|
||||
</top_modules>
|
||||
</db_ref>
|
||||
</db_ref_list>
|
||||
<zoom_setting>
|
||||
<ZoomStartTime time="3880386400fs"></ZoomStartTime>
|
||||
<ZoomEndTime time="4023922720fs"></ZoomEndTime>
|
||||
<Cursor1Time time="4000000000fs"></Cursor1Time>
|
||||
</zoom_setting>
|
||||
<column_width_setting>
|
||||
<NameColumnWidth column_width="209"></NameColumnWidth>
|
||||
<ValueColumnWidth column_width="90"></ValueColumnWidth>
|
||||
</column_width_setting>
|
||||
<WVObjectSize size="16" />
|
||||
<wvobject fp_name="/testbench/clk" type="logic">
|
||||
<obj_property name="ElementShortName">clk</obj_property>
|
||||
<obj_property name="ObjectShortName">clk</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/testbench/top0/cpu0/addr" type="array">
|
||||
<obj_property name="ElementShortName">addr[31:0]</obj_property>
|
||||
<obj_property name="ObjectShortName">addr[31:0]</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/testbench/top0/cpu0/data_in" type="array">
|
||||
<obj_property name="ElementShortName">data_in[31:0]</obj_property>
|
||||
<obj_property name="ObjectShortName">data_in[31:0]</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/testbench/top0/cpu0/data_out" type="array">
|
||||
<obj_property name="ElementShortName">data_out[31:0]</obj_property>
|
||||
<obj_property name="ObjectShortName">data_out[31:0]</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/testbench/top0/cpu0/seq_state" type="array">
|
||||
<obj_property name="ElementShortName">seq_state[1:0]</obj_property>
|
||||
<obj_property name="ObjectShortName">seq_state[1:0]</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/testbench/top0/cpu0/PC" type="array">
|
||||
<obj_property name="ElementShortName">PC[31:0]</obj_property>
|
||||
<obj_property name="ObjectShortName">PC[31:0]</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/testbench/top0/cpu0/nPC" type="array">
|
||||
<obj_property name="ElementShortName">nPC[31:0]</obj_property>
|
||||
<obj_property name="ObjectShortName">nPC[31:0]</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/testbench/top0/cpu0/ins" type="array">
|
||||
<obj_property name="ElementShortName">ins[15:0]</obj_property>
|
||||
<obj_property name="ObjectShortName">ins[15:0]</obj_property>
|
||||
<obj_property name="Radix">BINARYRADIX</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/testbench/top0/cpu0/ins_branch" type="logic">
|
||||
<obj_property name="ElementShortName">ins_branch</obj_property>
|
||||
<obj_property name="ObjectShortName">ins_branch</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/testbench/top0/cpu0/ins_loadrel" type="logic">
|
||||
<obj_property name="ElementShortName">ins_loadrel</obj_property>
|
||||
<obj_property name="ObjectShortName">ins_loadrel</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/testbench/top0/cpu0/X" type="array">
|
||||
<obj_property name="ElementShortName">X[31:0]</obj_property>
|
||||
<obj_property name="ObjectShortName">X[31:0]</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/testbench/top0/cpu0/nX" type="array">
|
||||
<obj_property name="ElementShortName">nX[31:0]</obj_property>
|
||||
<obj_property name="ObjectShortName">nX[31:0]</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/testbench/top0/cpu0/FP" type="array">
|
||||
<obj_property name="ElementShortName">FP[31:0]</obj_property>
|
||||
<obj_property name="ObjectShortName">FP[31:0]</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/testbench/top0/cpu0/pc_next_ins" type="array">
|
||||
<obj_property name="ElementShortName">pc_next_ins[31:0]</obj_property>
|
||||
<obj_property name="ObjectShortName">pc_next_ins[31:0]</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/testbench/top0/cpu0/mem_wait" type="logic">
|
||||
<obj_property name="ElementShortName">mem_wait</obj_property>
|
||||
<obj_property name="ObjectShortName">mem_wait</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="/testbench/top0/cpu0/ins_buf" type="array">
|
||||
<obj_property name="ElementShortName">ins_buf[15:0]</obj_property>
|
||||
<obj_property name="ObjectShortName">ins_buf[15:0]</obj_property>
|
||||
</wvobject>
|
||||
</wave_config>
|
||||
683
rtl/arty-a7/tridoracpu.tcl
Normal file
683
rtl/arty-a7/tridoracpu.tcl
Normal file
|
|
@ -0,0 +1,683 @@
|
|||
#*****************************************************************************************
|
||||
# Vivado (TM) v2020.1 (64-bit)
|
||||
#
|
||||
# tridoracpu.tcl: Tcl script for re-creating project 'tridoracpu'
|
||||
#
|
||||
# Generated by Vivado on Sat Sep 14 23:58:12 +0200 2024
|
||||
# IP Build 2902112 on Wed May 27 22:43:36 MDT 2020
|
||||
#
|
||||
# This file contains the Vivado Tcl commands for re-creating the project to the state*
|
||||
# when this script was generated. In order to re-create the project, please source this
|
||||
# file in the Vivado Tcl Shell.
|
||||
#
|
||||
# * Note that the runs in the created project will be configured the same way as the
|
||||
# original project, however they will not be launched automatically. To regenerate the
|
||||
# run results please launch the synthesis/implementation runs as needed.
|
||||
#
|
||||
#*****************************************************************************************
|
||||
|
||||
# uncomment next two statements if you have never initialized the Xilinx Board Store
|
||||
# this will take quite some time
|
||||
#xhub::refresh_catalog [xhub::get_xstores xilinx_board_store]
|
||||
#xhub::install [xhub::get_xitems]
|
||||
|
||||
# Set the reference directory for source file relative paths
|
||||
set origin_dir "change_this_to_your_rtl_directory"
|
||||
|
||||
set xilinx_board_store_dir [get_property LOCAL_ROOT_DIR [xhub::get_xstores xilinx_board_store]]
|
||||
set_param board.repoPaths [get_property LOCAL_ROOT_DIR [xhub::get_xstores xilinx_board_store]]
|
||||
|
||||
# Use origin directory path location variable, if specified in the tcl shell
|
||||
if { [info exists ::origin_dir_loc] } {
|
||||
set origin_dir $::origin_dir_loc
|
||||
}
|
||||
|
||||
# Set the project name
|
||||
set _xil_proj_name_ "tridoracpu"
|
||||
|
||||
# Use project name variable, if specified in the tcl shell
|
||||
if { [info exists ::user_project_name] } {
|
||||
set _xil_proj_name_ $::user_project_name
|
||||
}
|
||||
|
||||
variable script_file
|
||||
set script_file "tridoracpu.tcl"
|
||||
|
||||
# Help information for this script
|
||||
proc print_help {} {
|
||||
variable script_file
|
||||
puts "\nDescription:"
|
||||
puts "Recreate a Vivado project from this script. The created project will be"
|
||||
puts "functionally equivalent to the original project for which this script was"
|
||||
puts "generated. The script contains commands for creating a project, filesets,"
|
||||
puts "runs, adding/importing sources and setting properties on various objects.\n"
|
||||
puts "Syntax:"
|
||||
puts "$script_file"
|
||||
puts "$script_file -tclargs \[--origin_dir <path>\]"
|
||||
puts "$script_file -tclargs \[--project_name <name>\]"
|
||||
puts "$script_file -tclargs \[--help\]\n"
|
||||
puts "Usage:"
|
||||
puts "Name Description"
|
||||
puts "-------------------------------------------------------------------------"
|
||||
puts "\[--origin_dir <path>\] Determine source file paths wrt this path. Default"
|
||||
puts " origin_dir path value is \".\", otherwise, the value"
|
||||
puts " that was set with the \"-paths_relative_to\" switch"
|
||||
puts " when this script was generated.\n"
|
||||
puts "\[--project_name <name>\] Create project with the specified name. Default"
|
||||
puts " name is the name of the project from where this"
|
||||
puts " script was generated.\n"
|
||||
puts "\[--help\] Print help information for this script"
|
||||
puts "-------------------------------------------------------------------------\n"
|
||||
exit 0
|
||||
}
|
||||
|
||||
if { $::argc > 0 } {
|
||||
for {set i 0} {$i < $::argc} {incr i} {
|
||||
set option [string trim [lindex $::argv $i]]
|
||||
switch -regexp -- $option {
|
||||
"--origin_dir" { incr i; set origin_dir [lindex $::argv $i] }
|
||||
"--project_name" { incr i; set _xil_proj_name_ [lindex $::argv $i] }
|
||||
"--help" { print_help }
|
||||
default {
|
||||
if { [regexp {^-} $option] } {
|
||||
puts "ERROR: Unknown option '$option' specified, please type '$script_file -tclargs --help' for usage info.\n"
|
||||
return 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Set the directory path for the original project from where this script was exported
|
||||
set orig_proj_dir "[file normalize "${origin_dir}/arty-a7"]"
|
||||
|
||||
# Create project
|
||||
create_project ${_xil_proj_name_} ./${_xil_proj_name_} -part xc7a35ticsg324-1L
|
||||
|
||||
# Set the directory path for the new project
|
||||
set proj_dir [get_property directory [current_project]]
|
||||
|
||||
# Set project properties
|
||||
set obj [current_project]
|
||||
#set_property -name "board_part_repo_paths" -value "[file normalize "$xilinx_board_store_dir"]" -objects $obj
|
||||
set_property -name "board_part" -value "digilentinc.com:arty-a7-35:part0:1.0" -objects $obj
|
||||
set_property -name "default_lib" -value "xil_defaultlib" -objects $obj
|
||||
set_property -name "enable_vhdl_2008" -value "1" -objects $obj
|
||||
set_property -name "ip_cache_permissions" -value "read write" -objects $obj
|
||||
set_property -name "ip_output_repo" -value "$proj_dir/${_xil_proj_name_}.cache/ip" -objects $obj
|
||||
set_property -name "mem.enable_memory_map_generation" -value "1" -objects $obj
|
||||
set_property -name "platform.board_id" -value "arty-a7-35" -objects $obj
|
||||
set_property -name "sim.central_dir" -value "$proj_dir/${_xil_proj_name_}.ip_user_files" -objects $obj
|
||||
set_property -name "sim.ip.auto_export_scripts" -value "1" -objects $obj
|
||||
set_property -name "simulator_language" -value "Mixed" -objects $obj
|
||||
set_property -name "source_mgmt_mode" -value "DisplayOnly" -objects $obj
|
||||
set_property -name "webtalk.activehdl_export_sim" -value "4" -objects $obj
|
||||
set_property -name "webtalk.ies_export_sim" -value "4" -objects $obj
|
||||
set_property -name "webtalk.modelsim_export_sim" -value "4" -objects $obj
|
||||
set_property -name "webtalk.questa_export_sim" -value "4" -objects $obj
|
||||
set_property -name "webtalk.riviera_export_sim" -value "4" -objects $obj
|
||||
set_property -name "webtalk.vcs_export_sim" -value "4" -objects $obj
|
||||
set_property -name "webtalk.xsim_export_sim" -value "4" -objects $obj
|
||||
set_property -name "webtalk.xsim_launch_sim" -value "537" -objects $obj
|
||||
|
||||
# Create 'sources_1' fileset (if not found)
|
||||
if {[string equal [get_filesets -quiet sources_1] ""]} {
|
||||
create_fileset -srcset sources_1
|
||||
}
|
||||
|
||||
# Set 'sources_1' fileset object
|
||||
set obj [get_filesets sources_1]
|
||||
set files [list \
|
||||
[file normalize "${origin_dir}/src/uart.v"] \
|
||||
]
|
||||
add_files -norecurse -fileset $obj $files
|
||||
|
||||
# Add local files from the original project (-no_copy_sources specified)
|
||||
set files [list \
|
||||
[file normalize "${origin_dir}/src/cpuclk.v" ]\
|
||||
[file normalize "${origin_dir}/src/display_clock.v" ]\
|
||||
[file normalize "${origin_dir}/src/mem.v" ]\
|
||||
[file normalize "${origin_dir}/src/stack.v" ]\
|
||||
[file normalize "${origin_dir}/src/stackcpu.v" ]\
|
||||
[file normalize "${origin_dir}/src/vgafb.v" ]\
|
||||
[file normalize "${origin_dir}/src/top.v" ]\
|
||||
[file normalize "${origin_dir}/src/testbench.v" ]\
|
||||
[file normalize "${orig_proj_dir}/rom.mem" ]\
|
||||
[file normalize "${orig_proj_dir}/mig_dram_0/mig_a.prj" ]\
|
||||
[file normalize "${orig_proj_dir}/mig_dram_0/mig_b.prj" ]\
|
||||
[file normalize "${origin_dir}/src/dram_bridge.v" ]\
|
||||
[file normalize "${origin_dir}/src/sdspi.v" ]\
|
||||
[file normalize "${origin_dir}/src/bram_tdp.v" ]\
|
||||
[file normalize "${origin_dir}/src/palette.v" ]\
|
||||
[file normalize "${origin_dir}/src/irqctrl.v" ]\
|
||||
[file normalize "${origin_dir}/src/fifo.v" ]\
|
||||
[file normalize "${origin_dir}/src/fifo_testbench.v" ]\
|
||||
[file normalize "${origin_dir}/src/sdspi_testbench.v" ]\
|
||||
]
|
||||
set added_files [add_files -fileset sources_1 $files]
|
||||
|
||||
# Set 'sources_1' fileset file properties for remote files
|
||||
set file "$origin_dir/src/uart.v"
|
||||
set file [file normalize $file]
|
||||
set file_obj [get_files -of_objects [get_filesets sources_1] [list "*$file"]]
|
||||
set_property -name "used_in" -value "synthesis implementation" -objects $file_obj
|
||||
set_property -name "used_in_simulation" -value "0" -objects $file_obj
|
||||
|
||||
|
||||
# Set 'sources_1' fileset file properties for local files
|
||||
set file "src/cpuclk.v"
|
||||
set file_obj [get_files -of_objects [get_filesets sources_1] [list "*$file"]]
|
||||
set_property -name "used_in" -value "synthesis implementation" -objects $file_obj
|
||||
set_property -name "used_in_simulation" -value "0" -objects $file_obj
|
||||
|
||||
set file "src/display_clock.v"
|
||||
set file_obj [get_files -of_objects [get_filesets sources_1] [list "*$file"]]
|
||||
set_property -name "is_enabled" -value "0" -objects $file_obj
|
||||
|
||||
set file "src/mem.v"
|
||||
set file_obj [get_files -of_objects [get_filesets sources_1] [list "*$file"]]
|
||||
set_property -name "used_in" -value "synthesis implementation" -objects $file_obj
|
||||
set_property -name "used_in_simulation" -value "0" -objects $file_obj
|
||||
|
||||
set file "src/stack.v"
|
||||
set file_obj [get_files -of_objects [get_filesets sources_1] [list "*$file"]]
|
||||
set_property -name "used_in" -value "synthesis implementation" -objects $file_obj
|
||||
set_property -name "used_in_simulation" -value "0" -objects $file_obj
|
||||
|
||||
set file "src/stackcpu.v"
|
||||
set file_obj [get_files -of_objects [get_filesets sources_1] [list "*$file"]]
|
||||
set_property -name "used_in" -value "synthesis implementation" -objects $file_obj
|
||||
set_property -name "used_in_simulation" -value "0" -objects $file_obj
|
||||
|
||||
set file "src/vgafb.v"
|
||||
set file_obj [get_files -of_objects [get_filesets sources_1] [list "*$file"]]
|
||||
set_property -name "used_in" -value "synthesis implementation" -objects $file_obj
|
||||
set_property -name "used_in_simulation" -value "0" -objects $file_obj
|
||||
|
||||
set file "src/top.v"
|
||||
set file_obj [get_files -of_objects [get_filesets sources_1] [list "*$file"]]
|
||||
set_property -name "used_in" -value "synthesis implementation" -objects $file_obj
|
||||
set_property -name "used_in_simulation" -value "0" -objects $file_obj
|
||||
|
||||
set file "src/testbench.v"
|
||||
set file_obj [get_files -of_objects [get_filesets sources_1] [list "*$file"]]
|
||||
set_property -name "used_in" -value "" -objects $file_obj
|
||||
set_property -name "used_in_implementation" -value "0" -objects $file_obj
|
||||
set_property -name "used_in_simulation" -value "0" -objects $file_obj
|
||||
set_property -name "used_in_synthesis" -value "0" -objects $file_obj
|
||||
|
||||
set file "arty-a7/rom.mem"
|
||||
set file_obj [get_files -of_objects [get_filesets sources_1] [list "*$file"]]
|
||||
set_property -name "file_type" -value "Memory File" -objects $file_obj
|
||||
|
||||
set file "arty-a7/mig_dram_0/mig_a.prj"
|
||||
set file_obj [get_files -of_objects [get_filesets sources_1] [list "*$file"]]
|
||||
set_property -name "scoped_to_cells" -value "mig_dram_0" -objects $file_obj
|
||||
|
||||
set file "arty-a7/mig_dram_0/mig_b.prj"
|
||||
set file_obj [get_files -of_objects [get_filesets sources_1] [list "*$file"]]
|
||||
set_property -name "scoped_to_cells" -value "mig_dram_0" -objects $file_obj
|
||||
|
||||
set file "src/dram_bridge.v"
|
||||
set file_obj [get_files -of_objects [get_filesets sources_1] [list "*$file"]]
|
||||
set_property -name "used_in" -value "synthesis implementation" -objects $file_obj
|
||||
set_property -name "used_in_simulation" -value "0" -objects $file_obj
|
||||
|
||||
set file "src/palette.v"
|
||||
set file_obj [get_files -of_objects [get_filesets sources_1] [list "*$file"]]
|
||||
set_property -name "used_in" -value "synthesis implementation" -objects $file_obj
|
||||
set_property -name "used_in_simulation" -value "0" -objects $file_obj
|
||||
|
||||
set file "src/irqctrl.v"
|
||||
set file_obj [get_files -of_objects [get_filesets sources_1] [list "*$file"]]
|
||||
set_property -name "used_in" -value "synthesis implementation" -objects $file_obj
|
||||
set_property -name "used_in_simulation" -value "0" -objects $file_obj
|
||||
|
||||
set file "src/fifo_testbench.v"
|
||||
set file_obj [get_files -of_objects [get_filesets sources_1] [list "*$file"]]
|
||||
set_property -name "used_in" -value "" -objects $file_obj
|
||||
set_property -name "used_in_implementation" -value "0" -objects $file_obj
|
||||
set_property -name "used_in_simulation" -value "0" -objects $file_obj
|
||||
set_property -name "used_in_synthesis" -value "0" -objects $file_obj
|
||||
|
||||
|
||||
# Set 'sources_1' fileset properties
|
||||
set obj [get_filesets sources_1]
|
||||
set_property -name "top" -value "top" -objects $obj
|
||||
set_property -name "top_auto_set" -value "0" -objects $obj
|
||||
|
||||
# Set 'sources_1' fileset object
|
||||
set obj [get_filesets sources_1]
|
||||
# Add local files from the original project (-no_copy_sources specified)
|
||||
set files [list \
|
||||
[file normalize "${orig_proj_dir}/mig_dram_0/mig_dram_0.xci" ]\
|
||||
]
|
||||
set added_files [add_files -fileset sources_1 $files]
|
||||
|
||||
# Set 'sources_1' fileset file properties for remote files
|
||||
# None
|
||||
|
||||
# Set 'sources_1' fileset file properties for local files
|
||||
set file "arty-a7/mig_dram_0/mig_dram_0.xci"
|
||||
set file_obj [get_files -of_objects [get_filesets sources_1] [list "*$file"]]
|
||||
set_property -name "generate_files_for_reference" -value "0" -objects $file_obj
|
||||
set_property -name "registered_with_manager" -value "1" -objects $file_obj
|
||||
if { ![get_property "is_locked" $file_obj] } {
|
||||
set_property -name "synth_checkpoint_mode" -value "Singular" -objects $file_obj
|
||||
}
|
||||
set_property -name "used_in" -value "synthesis implementation" -objects $file_obj
|
||||
set_property -name "used_in_simulation" -value "0" -objects $file_obj
|
||||
|
||||
|
||||
# Create 'constrs_1' fileset (if not found)
|
||||
if {[string equal [get_filesets -quiet constrs_1] ""]} {
|
||||
create_fileset -constrset constrs_1
|
||||
}
|
||||
|
||||
# Set 'constrs_1' fileset object
|
||||
set obj [get_filesets constrs_1]
|
||||
|
||||
# Add/Import constrs file and set constrs file properties
|
||||
set file "[file normalize ${origin_dir}/arty-a7/Arty-A7-35-Master.xdc]"
|
||||
set file_added [add_files -norecurse -fileset $obj [list $file]]
|
||||
set file "$origin_dir/arty-a7/Arty-A7-35-Master.xdc"
|
||||
set file [file normalize $file]
|
||||
set file_obj [get_files -of_objects [get_filesets constrs_1] [list "*$file"]]
|
||||
set_property -name "file_type" -value "XDC" -objects $file_obj
|
||||
|
||||
# Set 'constrs_1' fileset properties
|
||||
set obj [get_filesets constrs_1]
|
||||
set_property -name "target_constrs_file" -value "$orig_proj_dir/Arty-A7-35-Master.xdc" -objects $obj
|
||||
set_property -name "target_ucf" -value "$orig_proj_dir/Arty-A7-35-Master.xdc" -objects $obj
|
||||
|
||||
# Create 'sim_1' fileset (if not found)
|
||||
if {[string equal [get_filesets -quiet sim_1] ""]} {
|
||||
create_fileset -simset sim_1
|
||||
}
|
||||
|
||||
# Set 'sim_1' fileset object
|
||||
set obj [get_filesets sim_1]
|
||||
# Add local files from the original project (-no_copy_sources specified)
|
||||
set files [list \
|
||||
[file normalize "${origin_dir}/src/uart_tb.v" ]\
|
||||
[file normalize "${orig_proj_dir}/testbench_behav1.wcfg" ]\
|
||||
]
|
||||
set added_files [add_files -fileset sim_1 $files]
|
||||
|
||||
# Set 'sim_1' fileset file properties for remote files
|
||||
# None
|
||||
|
||||
# Set 'sim_1' fileset file properties for local files
|
||||
set file [file normalize "${origin_dir}/src/uart_tb.v"]
|
||||
set file_obj [get_files -of_objects [get_filesets sim_1] [list "*$file"]]
|
||||
set_property -name "used_in" -value "" -objects $file_obj
|
||||
set_property -name "used_in_implementation" -value "0" -objects $file_obj
|
||||
set_property -name "used_in_simulation" -value "0" -objects $file_obj
|
||||
set_property -name "used_in_synthesis" -value "0" -objects $file_obj
|
||||
|
||||
|
||||
# Set 'sim_1' fileset properties
|
||||
set obj [get_filesets sim_1]
|
||||
set_property -name "hbs.configure_design_for_hier_access" -value "1" -objects $obj
|
||||
set_property -name "nl.mode" -value "funcsim" -objects $obj
|
||||
set_property -name "top" -value "testbench" -objects $obj
|
||||
set_property -name "top_lib" -value "xil_defaultlib" -objects $obj
|
||||
|
||||
# Create 'sim_fifo' fileset (if not found)
|
||||
if {[string equal [get_filesets -quiet sim_fifo] ""]} {
|
||||
create_fileset -simset sim_fifo
|
||||
}
|
||||
|
||||
# Set 'sim_fifo' fileset object
|
||||
set obj [get_filesets sim_fifo]
|
||||
# Add local files from the original project (-no_copy_sources specified)
|
||||
set files [list \
|
||||
[file normalize "${origin_dir}/src/fifo.v" ]\
|
||||
[file normalize "${origin_dir}/src/fifo_testbench.v" ]\
|
||||
]
|
||||
set added_files [add_files -fileset sim_fifo $files]
|
||||
|
||||
# Set 'sim_fifo' fileset file properties for remote files
|
||||
# None
|
||||
|
||||
# Set 'sim_fifo' fileset file properties for local files
|
||||
# None
|
||||
|
||||
# Set 'sim_fifo' fileset properties
|
||||
set obj [get_filesets sim_fifo]
|
||||
set_property -name "hbs.configure_design_for_hier_access" -value "1" -objects $obj
|
||||
set_property -name "top" -value "fifo_testbench" -objects $obj
|
||||
set_property -name "top_auto_set" -value "0" -objects $obj
|
||||
|
||||
# Create 'sim_sdspi' fileset (if not found)
|
||||
if {[string equal [get_filesets -quiet sim_sdspi] ""]} {
|
||||
create_fileset -simset sim_sdspi
|
||||
}
|
||||
|
||||
# Set 'sim_sdspi' fileset object
|
||||
set obj [get_filesets sim_sdspi]
|
||||
# Add local files from the original project (-no_copy_sources specified)
|
||||
set files [list \
|
||||
[file normalize "${orig_proj_dir}/sdspi_testbench_behav.wcfg" ]\
|
||||
]
|
||||
set added_files [add_files -fileset sim_sdspi $files]
|
||||
|
||||
# Set 'sim_sdspi' fileset file properties for remote files
|
||||
# None
|
||||
|
||||
# Set 'sim_sdspi' fileset file properties for local files
|
||||
# None
|
||||
|
||||
# Set 'sim_sdspi' fileset properties
|
||||
set obj [get_filesets sim_sdspi]
|
||||
set_property -name "hbs.configure_design_for_hier_access" -value "1" -objects $obj
|
||||
set_property -name "sim_mode" -value "post-synthesis" -objects $obj
|
||||
set_property -name "top" -value "sdspi_testbench" -objects $obj
|
||||
set_property -name "top_auto_set" -value "0" -objects $obj
|
||||
set_property -name "top_lib" -value "xil_defaultlib" -objects $obj
|
||||
set_property -name "xsim.simulate.runtime" -value "10ms" -objects $obj
|
||||
|
||||
# Set 'utils_1' fileset object
|
||||
set obj [get_filesets utils_1]
|
||||
# Empty (no sources present)
|
||||
|
||||
# Set 'utils_1' fileset properties
|
||||
set obj [get_filesets utils_1]
|
||||
|
||||
# Create 'synth_1' run (if not found)
|
||||
if {[string equal [get_runs -quiet synth_1] ""]} {
|
||||
create_run -name synth_1 -part xc7a35ticsg324-1L -flow {Vivado Synthesis 2020} -strategy "Vivado Synthesis Defaults" -report_strategy {No Reports} -constrset constrs_1
|
||||
} else {
|
||||
set_property strategy "Vivado Synthesis Defaults" [get_runs synth_1]
|
||||
set_property flow "Vivado Synthesis 2020" [get_runs synth_1]
|
||||
}
|
||||
set obj [get_runs synth_1]
|
||||
set_property set_report_strategy_name 1 $obj
|
||||
set_property report_strategy {Vivado Synthesis Default Reports} $obj
|
||||
set_property set_report_strategy_name 0 $obj
|
||||
# Create 'synth_1_synth_report_utilization_0' report (if not found)
|
||||
if { [ string equal [get_report_configs -of_objects [get_runs synth_1] synth_1_synth_report_utilization_0] "" ] } {
|
||||
create_report_config -report_name synth_1_synth_report_utilization_0 -report_type report_utilization:1.0 -steps synth_design -runs synth_1
|
||||
}
|
||||
set obj [get_report_configs -of_objects [get_runs synth_1] synth_1_synth_report_utilization_0]
|
||||
if { $obj != "" } {
|
||||
|
||||
}
|
||||
set obj [get_runs synth_1]
|
||||
set_property -name "needs_refresh" -value "1" -objects $obj
|
||||
set_property -name "strategy" -value "Vivado Synthesis Defaults" -objects $obj
|
||||
|
||||
# set the current synth run
|
||||
current_run -synthesis [get_runs synth_1]
|
||||
|
||||
# Create 'impl_1' run (if not found)
|
||||
if {[string equal [get_runs -quiet impl_1] ""]} {
|
||||
create_run -name impl_1 -part xc7a35ticsg324-1L -flow {Vivado Implementation 2020} -strategy "Performance_RefinePlacement" -report_strategy {No Reports} -constrset constrs_1 -parent_run synth_1
|
||||
} else {
|
||||
set_property strategy "Performance_RefinePlacement" [get_runs impl_1]
|
||||
set_property flow "Vivado Implementation 2020" [get_runs impl_1]
|
||||
}
|
||||
set obj [get_runs impl_1]
|
||||
set_property set_report_strategy_name 1 $obj
|
||||
set_property report_strategy {Vivado Implementation Default Reports} $obj
|
||||
set_property set_report_strategy_name 0 $obj
|
||||
# Create 'impl_1_init_report_timing_summary_0' report (if not found)
|
||||
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_init_report_timing_summary_0] "" ] } {
|
||||
create_report_config -report_name impl_1_init_report_timing_summary_0 -report_type report_timing_summary:1.0 -steps init_design -runs impl_1
|
||||
}
|
||||
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_init_report_timing_summary_0]
|
||||
if { $obj != "" } {
|
||||
set_property -name "is_enabled" -value "0" -objects $obj
|
||||
set_property -name "options.max_paths" -value "10" -objects $obj
|
||||
|
||||
}
|
||||
# Create 'impl_1_opt_report_drc_0' report (if not found)
|
||||
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_opt_report_drc_0] "" ] } {
|
||||
create_report_config -report_name impl_1_opt_report_drc_0 -report_type report_drc:1.0 -steps opt_design -runs impl_1
|
||||
}
|
||||
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_opt_report_drc_0]
|
||||
if { $obj != "" } {
|
||||
|
||||
}
|
||||
# Create 'impl_1_opt_report_timing_summary_0' report (if not found)
|
||||
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_opt_report_timing_summary_0] "" ] } {
|
||||
create_report_config -report_name impl_1_opt_report_timing_summary_0 -report_type report_timing_summary:1.0 -steps opt_design -runs impl_1
|
||||
}
|
||||
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_opt_report_timing_summary_0]
|
||||
if { $obj != "" } {
|
||||
set_property -name "is_enabled" -value "0" -objects $obj
|
||||
set_property -name "options.max_paths" -value "10" -objects $obj
|
||||
|
||||
}
|
||||
# Create 'impl_1_power_opt_report_timing_summary_0' report (if not found)
|
||||
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_power_opt_report_timing_summary_0] "" ] } {
|
||||
create_report_config -report_name impl_1_power_opt_report_timing_summary_0 -report_type report_timing_summary:1.0 -steps power_opt_design -runs impl_1
|
||||
}
|
||||
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_power_opt_report_timing_summary_0]
|
||||
if { $obj != "" } {
|
||||
set_property -name "is_enabled" -value "0" -objects $obj
|
||||
set_property -name "options.max_paths" -value "10" -objects $obj
|
||||
|
||||
}
|
||||
# Create 'impl_1_place_report_io_0' report (if not found)
|
||||
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_place_report_io_0] "" ] } {
|
||||
create_report_config -report_name impl_1_place_report_io_0 -report_type report_io:1.0 -steps place_design -runs impl_1
|
||||
}
|
||||
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_place_report_io_0]
|
||||
if { $obj != "" } {
|
||||
|
||||
}
|
||||
# Create 'impl_1_place_report_utilization_0' report (if not found)
|
||||
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_place_report_utilization_0] "" ] } {
|
||||
create_report_config -report_name impl_1_place_report_utilization_0 -report_type report_utilization:1.0 -steps place_design -runs impl_1
|
||||
}
|
||||
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_place_report_utilization_0]
|
||||
if { $obj != "" } {
|
||||
|
||||
}
|
||||
# Create 'impl_1_place_report_control_sets_0' report (if not found)
|
||||
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_place_report_control_sets_0] "" ] } {
|
||||
create_report_config -report_name impl_1_place_report_control_sets_0 -report_type report_control_sets:1.0 -steps place_design -runs impl_1
|
||||
}
|
||||
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_place_report_control_sets_0]
|
||||
if { $obj != "" } {
|
||||
set_property -name "options.verbose" -value "1" -objects $obj
|
||||
|
||||
}
|
||||
# Create 'impl_1_place_report_incremental_reuse_0' report (if not found)
|
||||
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_place_report_incremental_reuse_0] "" ] } {
|
||||
create_report_config -report_name impl_1_place_report_incremental_reuse_0 -report_type report_incremental_reuse:1.0 -steps place_design -runs impl_1
|
||||
}
|
||||
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_place_report_incremental_reuse_0]
|
||||
if { $obj != "" } {
|
||||
set_property -name "is_enabled" -value "0" -objects $obj
|
||||
|
||||
}
|
||||
# Create 'impl_1_place_report_incremental_reuse_1' report (if not found)
|
||||
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_place_report_incremental_reuse_1] "" ] } {
|
||||
create_report_config -report_name impl_1_place_report_incremental_reuse_1 -report_type report_incremental_reuse:1.0 -steps place_design -runs impl_1
|
||||
}
|
||||
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_place_report_incremental_reuse_1]
|
||||
if { $obj != "" } {
|
||||
set_property -name "is_enabled" -value "0" -objects $obj
|
||||
|
||||
}
|
||||
# Create 'impl_1_place_report_timing_summary_0' report (if not found)
|
||||
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_place_report_timing_summary_0] "" ] } {
|
||||
create_report_config -report_name impl_1_place_report_timing_summary_0 -report_type report_timing_summary:1.0 -steps place_design -runs impl_1
|
||||
}
|
||||
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_place_report_timing_summary_0]
|
||||
if { $obj != "" } {
|
||||
set_property -name "is_enabled" -value "0" -objects $obj
|
||||
set_property -name "options.max_paths" -value "10" -objects $obj
|
||||
|
||||
}
|
||||
# Create 'impl_1_post_place_power_opt_report_timing_summary_0' report (if not found)
|
||||
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_post_place_power_opt_report_timing_summary_0] "" ] } {
|
||||
create_report_config -report_name impl_1_post_place_power_opt_report_timing_summary_0 -report_type report_timing_summary:1.0 -steps post_place_power_opt_design -runs impl_1
|
||||
}
|
||||
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_post_place_power_opt_report_timing_summary_0]
|
||||
if { $obj != "" } {
|
||||
set_property -name "is_enabled" -value "0" -objects $obj
|
||||
set_property -name "options.max_paths" -value "10" -objects $obj
|
||||
|
||||
}
|
||||
# Create 'impl_1_phys_opt_report_timing_summary_0' report (if not found)
|
||||
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_phys_opt_report_timing_summary_0] "" ] } {
|
||||
create_report_config -report_name impl_1_phys_opt_report_timing_summary_0 -report_type report_timing_summary:1.0 -steps phys_opt_design -runs impl_1
|
||||
}
|
||||
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_phys_opt_report_timing_summary_0]
|
||||
if { $obj != "" } {
|
||||
set_property -name "is_enabled" -value "0" -objects $obj
|
||||
set_property -name "options.max_paths" -value "10" -objects $obj
|
||||
|
||||
}
|
||||
# Create 'impl_1_route_report_drc_0' report (if not found)
|
||||
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_route_report_drc_0] "" ] } {
|
||||
create_report_config -report_name impl_1_route_report_drc_0 -report_type report_drc:1.0 -steps route_design -runs impl_1
|
||||
}
|
||||
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_route_report_drc_0]
|
||||
if { $obj != "" } {
|
||||
|
||||
}
|
||||
# Create 'impl_1_route_report_methodology_0' report (if not found)
|
||||
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_route_report_methodology_0] "" ] } {
|
||||
create_report_config -report_name impl_1_route_report_methodology_0 -report_type report_methodology:1.0 -steps route_design -runs impl_1
|
||||
}
|
||||
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_route_report_methodology_0]
|
||||
if { $obj != "" } {
|
||||
|
||||
}
|
||||
# Create 'impl_1_route_report_power_0' report (if not found)
|
||||
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_route_report_power_0] "" ] } {
|
||||
create_report_config -report_name impl_1_route_report_power_0 -report_type report_power:1.0 -steps route_design -runs impl_1
|
||||
}
|
||||
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_route_report_power_0]
|
||||
if { $obj != "" } {
|
||||
|
||||
}
|
||||
# Create 'impl_1_route_report_route_status_0' report (if not found)
|
||||
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_route_report_route_status_0] "" ] } {
|
||||
create_report_config -report_name impl_1_route_report_route_status_0 -report_type report_route_status:1.0 -steps route_design -runs impl_1
|
||||
}
|
||||
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_route_report_route_status_0]
|
||||
if { $obj != "" } {
|
||||
|
||||
}
|
||||
# Create 'impl_1_route_report_timing_summary_0' report (if not found)
|
||||
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_route_report_timing_summary_0] "" ] } {
|
||||
create_report_config -report_name impl_1_route_report_timing_summary_0 -report_type report_timing_summary:1.0 -steps route_design -runs impl_1
|
||||
}
|
||||
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_route_report_timing_summary_0]
|
||||
if { $obj != "" } {
|
||||
set_property -name "options.max_paths" -value "10" -objects $obj
|
||||
|
||||
}
|
||||
# Create 'impl_1_route_report_incremental_reuse_0' report (if not found)
|
||||
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_route_report_incremental_reuse_0] "" ] } {
|
||||
create_report_config -report_name impl_1_route_report_incremental_reuse_0 -report_type report_incremental_reuse:1.0 -steps route_design -runs impl_1
|
||||
}
|
||||
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_route_report_incremental_reuse_0]
|
||||
if { $obj != "" } {
|
||||
|
||||
}
|
||||
# Create 'impl_1_route_report_clock_utilization_0' report (if not found)
|
||||
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_route_report_clock_utilization_0] "" ] } {
|
||||
create_report_config -report_name impl_1_route_report_clock_utilization_0 -report_type report_clock_utilization:1.0 -steps route_design -runs impl_1
|
||||
}
|
||||
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_route_report_clock_utilization_0]
|
||||
if { $obj != "" } {
|
||||
|
||||
}
|
||||
# Create 'impl_1_route_report_bus_skew_0' report (if not found)
|
||||
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_route_report_bus_skew_0] "" ] } {
|
||||
create_report_config -report_name impl_1_route_report_bus_skew_0 -report_type report_bus_skew:1.1 -steps route_design -runs impl_1
|
||||
}
|
||||
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_route_report_bus_skew_0]
|
||||
if { $obj != "" } {
|
||||
set_property -name "options.warn_on_violation" -value "1" -objects $obj
|
||||
|
||||
}
|
||||
# Create 'impl_1_post_route_phys_opt_report_timing_summary_0' report (if not found)
|
||||
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_post_route_phys_opt_report_timing_summary_0] "" ] } {
|
||||
create_report_config -report_name impl_1_post_route_phys_opt_report_timing_summary_0 -report_type report_timing_summary:1.0 -steps post_route_phys_opt_design -runs impl_1
|
||||
}
|
||||
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_post_route_phys_opt_report_timing_summary_0]
|
||||
if { $obj != "" } {
|
||||
set_property -name "options.max_paths" -value "10" -objects $obj
|
||||
set_property -name "options.warn_on_violation" -value "1" -objects $obj
|
||||
|
||||
}
|
||||
# Create 'impl_1_post_route_phys_opt_report_bus_skew_0' report (if not found)
|
||||
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_post_route_phys_opt_report_bus_skew_0] "" ] } {
|
||||
create_report_config -report_name impl_1_post_route_phys_opt_report_bus_skew_0 -report_type report_bus_skew:1.1 -steps post_route_phys_opt_design -runs impl_1
|
||||
}
|
||||
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_post_route_phys_opt_report_bus_skew_0]
|
||||
if { $obj != "" } {
|
||||
set_property -name "options.warn_on_violation" -value "1" -objects $obj
|
||||
|
||||
}
|
||||
set obj [get_runs impl_1]
|
||||
set_property -name "needs_refresh" -value "1" -objects $obj
|
||||
set_property -name "strategy" -value "Performance_RefinePlacement" -objects $obj
|
||||
set_property -name "steps.place_design.args.directive" -value "ExtraPostPlacementOpt" -objects $obj
|
||||
set_property -name "steps.phys_opt_design.args.directive" -value "Explore" -objects $obj
|
||||
set_property -name "steps.route_design.args.directive" -value "Explore" -objects $obj
|
||||
set_property -name "steps.write_bitstream.args.bin_file" -value "1" -objects $obj
|
||||
set_property -name "steps.write_bitstream.args.readback_file" -value "0" -objects $obj
|
||||
set_property -name "steps.write_bitstream.args.verbose" -value "0" -objects $obj
|
||||
|
||||
# set the current impl run
|
||||
current_run -implementation [get_runs impl_1]
|
||||
|
||||
puts "INFO: Project created:${_xil_proj_name_}"
|
||||
# Create 'drc_1' gadget (if not found)
|
||||
if {[string equal [get_dashboard_gadgets [ list "drc_1" ] ] ""]} {
|
||||
create_dashboard_gadget -name {drc_1} -type drc
|
||||
}
|
||||
set obj [get_dashboard_gadgets [ list "drc_1" ] ]
|
||||
set_property -name "reports" -value "impl_1#impl_1_route_report_drc_0" -objects $obj
|
||||
|
||||
# Create 'methodology_1' gadget (if not found)
|
||||
if {[string equal [get_dashboard_gadgets [ list "methodology_1" ] ] ""]} {
|
||||
create_dashboard_gadget -name {methodology_1} -type methodology
|
||||
}
|
||||
set obj [get_dashboard_gadgets [ list "methodology_1" ] ]
|
||||
set_property -name "reports" -value "impl_1#impl_1_route_report_methodology_0" -objects $obj
|
||||
|
||||
# Create 'power_1' gadget (if not found)
|
||||
if {[string equal [get_dashboard_gadgets [ list "power_1" ] ] ""]} {
|
||||
create_dashboard_gadget -name {power_1} -type power
|
||||
}
|
||||
set obj [get_dashboard_gadgets [ list "power_1" ] ]
|
||||
set_property -name "reports" -value "impl_1#impl_1_route_report_power_0" -objects $obj
|
||||
|
||||
# Create 'timing_1' gadget (if not found)
|
||||
if {[string equal [get_dashboard_gadgets [ list "timing_1" ] ] ""]} {
|
||||
create_dashboard_gadget -name {timing_1} -type timing
|
||||
}
|
||||
set obj [get_dashboard_gadgets [ list "timing_1" ] ]
|
||||
set_property -name "reports" -value "impl_1#impl_1_route_report_timing_summary_0" -objects $obj
|
||||
|
||||
# Create 'utilization_1' gadget (if not found)
|
||||
if {[string equal [get_dashboard_gadgets [ list "utilization_1" ] ] ""]} {
|
||||
create_dashboard_gadget -name {utilization_1} -type utilization
|
||||
}
|
||||
set obj [get_dashboard_gadgets [ list "utilization_1" ] ]
|
||||
set_property -name "reports" -value "synth_1#synth_1_synth_report_utilization_0" -objects $obj
|
||||
set_property -name "run.step" -value "synth_design" -objects $obj
|
||||
set_property -name "run.type" -value "synthesis" -objects $obj
|
||||
|
||||
# Create 'utilization_2' gadget (if not found)
|
||||
if {[string equal [get_dashboard_gadgets [ list "utilization_2" ] ] ""]} {
|
||||
create_dashboard_gadget -name {utilization_2} -type utilization
|
||||
}
|
||||
set obj [get_dashboard_gadgets [ list "utilization_2" ] ]
|
||||
set_property -name "reports" -value "impl_1#impl_1_place_report_utilization_0" -objects $obj
|
||||
|
||||
move_dashboard_gadget -name {utilization_1} -row 0 -col 0
|
||||
move_dashboard_gadget -name {power_1} -row 1 -col 0
|
||||
move_dashboard_gadget -name {drc_1} -row 2 -col 0
|
||||
move_dashboard_gadget -name {timing_1} -row 0 -col 1
|
||||
move_dashboard_gadget -name {utilization_2} -row 1 -col 1
|
||||
move_dashboard_gadget -name {methodology_1} -row 2 -col 1
|
||||
46
rtl/src/bram_tdp.v
Normal file
46
rtl/src/bram_tdp.v
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
`timescale 1ns / 1ps
|
||||
// taken from https://danstrother.com/2010/09/11/inferring-rams-in-fpgas/
|
||||
// modified for one read/write-port and one read-only-port,
|
||||
/// A parameterized, inferable, true dual-port, dual-clock block RAM in Verilog.
|
||||
|
||||
module bram_tdp #(
|
||||
parameter DATA = 72,
|
||||
parameter ADDR = 10
|
||||
) (
|
||||
// Port A
|
||||
input wire a_clk,
|
||||
input wire a_rd,
|
||||
input wire a_wr,
|
||||
input wire [ADDR-1:0] a_addr,
|
||||
input wire [DATA-1:0] a_din,
|
||||
output reg [DATA-1:0] a_dout,
|
||||
// Port B
|
||||
input wire b_clk,
|
||||
input wire [ADDR-1:0] b_addr,
|
||||
output reg [DATA-1:0] b_dout,
|
||||
input wire b_rd
|
||||
);
|
||||
|
||||
// Shared memory
|
||||
reg [DATA-1:0] mem [(2**ADDR)-1:0];
|
||||
|
||||
wire a_en = a_rd || a_wr;
|
||||
|
||||
// Port A
|
||||
always @(posedge a_clk) begin
|
||||
if(a_en)
|
||||
begin
|
||||
if(a_wr)
|
||||
mem[a_addr] <= a_din;
|
||||
else if(a_rd)
|
||||
a_dout <= mem[a_addr];
|
||||
end
|
||||
end
|
||||
|
||||
// Port B
|
||||
always @(posedge b_clk) begin
|
||||
if(b_rd)
|
||||
b_dout <= mem[b_addr];
|
||||
end
|
||||
|
||||
endmodule
|
||||
83
rtl/src/cpuclk.v
Normal file
83
rtl/src/cpuclk.v
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
`timescale 1ns / 1ps
|
||||
|
||||
module cpu_clkgen(
|
||||
input wire rst,
|
||||
input wire clk100,
|
||||
output wire cpuclk,
|
||||
output wire dram_refclk,
|
||||
output wire pixclk,
|
||||
output wire locked
|
||||
);
|
||||
|
||||
wire cpuclk_pre, clk_fb, refclk_pre, pixclk_pre;
|
||||
|
||||
MMCME2_BASE #(
|
||||
.BANDWIDTH("OPTIMIZED"), // Jitter programming (OPTIMIZED, HIGH, LOW)
|
||||
.CLKFBOUT_MULT_F(10.0), // Multiply value for all CLKOUT (2.000-64.000).
|
||||
.CLKFBOUT_PHASE(0.0), // Phase offset in degrees of CLKFB (-360.000-360.000).
|
||||
.CLKIN1_PERIOD(10.0), // Input clock period in ns to ps resolution (i.e. 33.333 is 30 MHz).
|
||||
// CLKOUT0_DIVIDE - CLKOUT6_DIVIDE: Divide amount for each CLKOUT (1-128)
|
||||
.CLKOUT0_DIVIDE_F(12.0), // Divide amount for CLKOUT0 (1.000-128.000).
|
||||
.CLKOUT1_DIVIDE(5),
|
||||
.CLKOUT2_DIVIDE(40), // 40 = 25MHz pixel clock (should be 25.175MHz per spec) for 640x480
|
||||
//.CLKOUT2_DIVIDE(25), // 25 = 40MHz pixel clock for 800x600
|
||||
//.CLKOUT2_DIVIDE(15), // 15 = 66.66MHz pixel clock (should be 65.0Mhz per spec) for 1024x768
|
||||
.CLKOUT3_DIVIDE(1),
|
||||
.CLKOUT4_DIVIDE(1),
|
||||
.CLKOUT5_DIVIDE(1),
|
||||
.CLKOUT6_DIVIDE(1),
|
||||
// CLKOUT0_DUTY_CYCLE - CLKOUT6_DUTY_CYCLE: Duty cycle for each CLKOUT (0.01-0.99).
|
||||
.CLKOUT0_DUTY_CYCLE(0.5),
|
||||
.CLKOUT1_DUTY_CYCLE(0.5),
|
||||
.CLKOUT2_DUTY_CYCLE(0.5),
|
||||
.CLKOUT3_DUTY_CYCLE(0.5),
|
||||
.CLKOUT4_DUTY_CYCLE(0.5),
|
||||
.CLKOUT5_DUTY_CYCLE(0.5),
|
||||
.CLKOUT6_DUTY_CYCLE(0.5),
|
||||
// CLKOUT0_PHASE - CLKOUT6_PHASE: Phase offset for each CLKOUT (-360.000-360.000).
|
||||
.CLKOUT0_PHASE(0.0),
|
||||
.CLKOUT1_PHASE(0.0),
|
||||
.CLKOUT2_PHASE(0.0),
|
||||
.CLKOUT3_PHASE(0.0),
|
||||
.CLKOUT4_PHASE(0.0),
|
||||
.CLKOUT5_PHASE(0.0),
|
||||
.CLKOUT6_PHASE(0.0),
|
||||
.CLKOUT4_CASCADE("FALSE"), // Cascade CLKOUT4 counter with CLKOUT6 (FALSE, TRUE)
|
||||
.DIVCLK_DIVIDE(1), // Master division value (1-106)
|
||||
.REF_JITTER1(0.010), // Reference input jitter in UI (0.000-0.999).
|
||||
.STARTUP_WAIT("FALSE") // Delays DONE until MMCM is locked (FALSE, TRUE)
|
||||
)
|
||||
|
||||
MMCME2_BASE_inst (
|
||||
/* verilator lint_off PINCONNECTEMPTY */
|
||||
// Clock Outputs: 1-bit (each) output: User configurable clock outputs
|
||||
.CLKOUT0(cpuclk_pre), // 1-bit output: CLKOUT0
|
||||
.CLKOUT0B(), // 1-bit output: Inverted CLKOUT0
|
||||
.CLKOUT1(refclk_pre), // 1-bit output: CLKOUT1
|
||||
.CLKOUT1B(), // 1-bit output: Inverted CLKOUT1
|
||||
.CLKOUT2(pixclk_pre), // 1-bit output: CLKOUT2
|
||||
.CLKOUT2B(), // 1-bit output: Inverted CLKOUT2
|
||||
.CLKOUT3(), // 1-bit output: CLKOUT3
|
||||
.CLKOUT3B(), // 1-bit output: Inverted CLKOUT3
|
||||
.CLKOUT4(), // 1-bit output: CLKOUT4
|
||||
.CLKOUT5(), // 1-bit output: CLKOUT5
|
||||
.CLKOUT6(), // 1-bit output: CLKOUT6
|
||||
// Feedback Clocks: 1-bit (each) output: Clock feedback ports
|
||||
.CLKFBOUT(clk_fb), // 1-bit output: Feedback clock
|
||||
.CLKFBOUTB(), // 1-bit output: Inverted CLKFBOUT
|
||||
// Status Ports: 1-bit (each) output: MMCM status ports
|
||||
.LOCKED(locked), // 1-bit output: LOCK
|
||||
// Clock Inputs: 1-bit (each) input: Clock input
|
||||
.CLKIN1(clk100), // 1-bit input: Clock
|
||||
// Control Ports: 1-bit (each) input: MMCM control ports
|
||||
.PWRDWN(), // 1-bit input: Power-down
|
||||
/* verilator lint_on PINCONNECTEMPTY */
|
||||
.RST(rst), // 1-bit input: Reset
|
||||
// Feedback Clocks: 1-bit (each) input: Clock feedback ports
|
||||
.CLKFBIN(clk_fb) // 1-bit input: Feedback clock
|
||||
);
|
||||
|
||||
BUFG bufg_cpuclk(.I(cpuclk_pre), .O(cpuclk));
|
||||
BUFG bufg_refclk(.I(refclk_pre), .O(dram_refclk));
|
||||
BUFG bufg_pixclk(.I(pixclk_pre), .O(pixclk));
|
||||
endmodule
|
||||
96
rtl/src/display_clock.v
Normal file
96
rtl/src/display_clock.v
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
// Project F: Display Clocks
|
||||
// (C)2019 Will Green, Open source hardware released under the MIT License
|
||||
// Learn more at https://projectf.io
|
||||
|
||||
// Defaults to 25.2 and 126 MHz for 640x480 at 60 Hz
|
||||
|
||||
module display_clock #(
|
||||
MULT_MASTER=31.5, // master clock multiplier (2.000-64.000)
|
||||
DIV_MASTER=5, // master clock divider (1-106)
|
||||
DIV_5X=5.0, // 5x clock divider (1-128)
|
||||
DIV_1X=25, // 1x clock divider (1-128)
|
||||
IN_PERIOD=10.0 // period of i_clk in ns (100 MHz = 10.0 ns)
|
||||
)
|
||||
(
|
||||
input wire i_clk, // input clock
|
||||
input wire i_rst, // reset (active high)
|
||||
output wire o_clk_1x, // pixel clock
|
||||
output wire o_clk_5x, // 5x clock for 10:1 DDR SerDes
|
||||
output wire o_locked // clock locked? (active high)
|
||||
);
|
||||
|
||||
wire clk_fb; // internal clock feedback
|
||||
wire clk_1x_pre;
|
||||
wire clk_5x_pre;
|
||||
|
||||
MMCME2_BASE #(
|
||||
.BANDWIDTH("OPTIMIZED"), // Jitter programming (OPTIMIZED, HIGH, LOW)
|
||||
.CLKFBOUT_MULT_F(MULT_MASTER), // Multiply value for all CLKOUT (2.000-64.000).
|
||||
.CLKFBOUT_PHASE(0.0), // Phase offset in degrees of CLKFB (-360.000-360.000).
|
||||
.CLKIN1_PERIOD(IN_PERIOD), // Input clock period in ns to ps resolution (i.e. 33.333 is 30 MHz).
|
||||
// CLKOUT0_DIVIDE - CLKOUT6_DIVIDE: Divide amount for each CLKOUT (1-128)
|
||||
.CLKOUT0_DIVIDE_F(DIV_5X), // Divide amount for CLKOUT0 (1.000-128.000).
|
||||
.CLKOUT1_DIVIDE(DIV_1X),
|
||||
.CLKOUT2_DIVIDE(1),
|
||||
.CLKOUT3_DIVIDE(1),
|
||||
.CLKOUT4_DIVIDE(1),
|
||||
.CLKOUT5_DIVIDE(1),
|
||||
.CLKOUT6_DIVIDE(1),
|
||||
// CLKOUT0_DUTY_CYCLE - CLKOUT6_DUTY_CYCLE: Duty cycle for each CLKOUT (0.01-0.99).
|
||||
.CLKOUT0_DUTY_CYCLE(0.5),
|
||||
.CLKOUT1_DUTY_CYCLE(0.5),
|
||||
.CLKOUT2_DUTY_CYCLE(0.5),
|
||||
.CLKOUT3_DUTY_CYCLE(0.5),
|
||||
.CLKOUT4_DUTY_CYCLE(0.5),
|
||||
.CLKOUT5_DUTY_CYCLE(0.5),
|
||||
.CLKOUT6_DUTY_CYCLE(0.5),
|
||||
// CLKOUT0_PHASE - CLKOUT6_PHASE: Phase offset for each CLKOUT (-360.000-360.000).
|
||||
.CLKOUT0_PHASE(0.0),
|
||||
.CLKOUT1_PHASE(0.0),
|
||||
.CLKOUT2_PHASE(0.0),
|
||||
.CLKOUT3_PHASE(0.0),
|
||||
.CLKOUT4_PHASE(0.0),
|
||||
.CLKOUT5_PHASE(0.0),
|
||||
.CLKOUT6_PHASE(0.0),
|
||||
.CLKOUT4_CASCADE("FALSE"), // Cascade CLKOUT4 counter with CLKOUT6 (FALSE, TRUE)
|
||||
.DIVCLK_DIVIDE(DIV_MASTER), // Master division value (1-106)
|
||||
.REF_JITTER1(0.010), // Reference input jitter in UI (0.000-0.999).
|
||||
.STARTUP_WAIT("FALSE") // Delays DONE until MMCM is locked (FALSE, TRUE)
|
||||
)
|
||||
MMCME2_BASE_inst (
|
||||
/* verilator lint_off PINCONNECTEMPTY */
|
||||
// Clock Outputs: 1-bit (each) output: User configurable clock outputs
|
||||
.CLKOUT0(clk_5x_pre), // 1-bit output: CLKOUT0
|
||||
.CLKOUT0B(), // 1-bit output: Inverted CLKOUT0
|
||||
.CLKOUT1(clk_1x_pre), // 1-bit output: CLKOUT1
|
||||
.CLKOUT1B(), // 1-bit output: Inverted CLKOUT1
|
||||
.CLKOUT2(), // 1-bit output: CLKOUT2
|
||||
.CLKOUT2B(), // 1-bit output: Inverted CLKOUT2
|
||||
.CLKOUT3(), // 1-bit output: CLKOUT3
|
||||
.CLKOUT3B(), // 1-bit output: Inverted CLKOUT3
|
||||
.CLKOUT4(), // 1-bit output: CLKOUT4
|
||||
.CLKOUT5(), // 1-bit output: CLKOUT5
|
||||
.CLKOUT6(), // 1-bit output: CLKOUT6
|
||||
// Feedback Clocks: 1-bit (each) output: Clock feedback ports
|
||||
.CLKFBOUT(clk_fb), // 1-bit output: Feedback clock
|
||||
.CLKFBOUTB(), // 1-bit output: Inverted CLKFBOUT
|
||||
// Status Ports: 1-bit (each) output: MMCM status ports
|
||||
.LOCKED(o_locked), // 1-bit output: LOCK
|
||||
// Clock Inputs: 1-bit (each) input: Clock input
|
||||
.CLKIN1(i_clk), // 1-bit input: Clock
|
||||
// Control Ports: 1-bit (each) input: MMCM control ports
|
||||
.PWRDWN(), // 1-bit input: Power-down
|
||||
/* verilator lint_on PINCONNECTEMPTY */
|
||||
.RST(i_rst), // 1-bit input: Reset
|
||||
// Feedback Clocks: 1-bit (each) input: Clock feedback ports
|
||||
.CLKFBIN(clk_fb) // 1-bit input: Feedback clock
|
||||
);
|
||||
|
||||
// explicitly buffer output clocks
|
||||
BUFG bufg_clk_pix(.I(clk_1x_pre), .O(o_clk_1x));
|
||||
BUFG bufg_clk_pix_5x(.I(clk_5x_pre), .O(o_clk_5x));
|
||||
|
||||
endmodule
|
||||
165
rtl/src/dram_bridge.v
Normal file
165
rtl/src/dram_bridge.v
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
`timescale 1ns / 1ps
|
||||
|
||||
module dram_bridge #(ADDR_WIDTH = 32, WIDTH = 32)
|
||||
(
|
||||
// local bus
|
||||
input wire [ADDR_WIDTH-1:0] mem_addr,
|
||||
output wire [WIDTH-1:0] mem_read_data,
|
||||
input wire [WIDTH-1:0] mem_write_data,
|
||||
input wire mem_read_enable,
|
||||
input wire mem_write_enable,
|
||||
output wire mem_wait,
|
||||
|
||||
input wire rst_n,
|
||||
input wire dram_front_clk,
|
||||
input wire dram_refclk,
|
||||
|
||||
// DDR3 SDRAM
|
||||
inout wire [15:0] ddr3_dq,
|
||||
inout wire [1:0] ddr3_dqs_n,
|
||||
inout wire [1:0] ddr3_dqs_p,
|
||||
|
||||
output wire [13:0] ddr3_addr,
|
||||
output wire [2:0] ddr3_ba,
|
||||
output wire ddr3_ras_n,
|
||||
output wire ddr3_cas_n,
|
||||
output wire ddr3_we_n,
|
||||
output wire ddr3_reset_n,
|
||||
output wire [0:0] ddr3_ck_p,
|
||||
output wire [0:0] ddr3_ck_n,
|
||||
output wire [0:0] ddr3_cke,
|
||||
output wire [0:0] ddr3_cs_n,
|
||||
output wire [1:0] ddr3_dm,
|
||||
output wire [0:0] ddr3_odt
|
||||
);
|
||||
|
||||
localparam DRAM_ADDR_WIDTH = 28, DRAM_DATA_WIDTH = 128, DRAM_MASK_WIDTH = 16;
|
||||
wire [DRAM_ADDR_WIDTH-1:0] app_addr;
|
||||
wire [2:0] app_cmd;
|
||||
wire app_en;
|
||||
wire app_rdy;
|
||||
wire [DRAM_DATA_WIDTH-1:0] app_rd_data;
|
||||
wire app_rd_data_end;
|
||||
wire app_rd_data_valid;
|
||||
wire [DRAM_DATA_WIDTH-1:0] app_wdf_data;
|
||||
wire app_wdf_end;
|
||||
wire [DRAM_MASK_WIDTH-1:0] app_wdf_mask;
|
||||
wire app_wdf_rdy;
|
||||
wire app_sr_active;
|
||||
wire app_ref_ack;
|
||||
wire app_zq_ack;
|
||||
wire app_wdf_wren;
|
||||
wire [11:0] device_temp;
|
||||
wire ui_clk, ui_rst_sync;
|
||||
wire init_calib_complete;
|
||||
|
||||
localparam CMD_READ = 3'b1;
|
||||
localparam CMD_WRITE = 3'b0;
|
||||
|
||||
mig_dram_0 dram0(
|
||||
// Inouts
|
||||
.ddr3_dq(ddr3_dq),
|
||||
.ddr3_dqs_n(ddr3_dqs_n),
|
||||
.ddr3_dqs_p(ddr3_dqs_p),
|
||||
// Outputs
|
||||
.ddr3_addr(ddr3_addr),
|
||||
.ddr3_ba(ddr3_ba),
|
||||
.ddr3_ras_n(ddr3_ras_n),
|
||||
.ddr3_cas_n(ddr3_cas_n),
|
||||
.ddr3_we_n(ddr3_we_n),
|
||||
.ddr3_reset_n(ddr3_reset_n),
|
||||
.ddr3_ck_p(ddr3_ck_p),
|
||||
.ddr3_ck_n(ddr3_ck_n),
|
||||
.ddr3_cke(ddr3_cke),
|
||||
.ddr3_cs_n(ddr3_cs_n),
|
||||
.ddr3_dm(ddr3_dm),
|
||||
.ddr3_odt(ddr3_odt),
|
||||
// Application interface ports
|
||||
.app_addr (app_addr),
|
||||
.app_cmd (app_cmd),
|
||||
.app_en (app_en),
|
||||
.app_wdf_data (app_wdf_data),
|
||||
.app_wdf_mask (app_wdf_mask),
|
||||
.app_wdf_end (app_wdf_end),
|
||||
.app_wdf_wren (app_wdf_wren),
|
||||
.app_rd_data (app_rd_data),
|
||||
.app_rd_data_end (app_rd_data_end),
|
||||
.app_rd_data_valid (app_rd_data_valid),
|
||||
.app_rdy (app_rdy),
|
||||
.app_wdf_rdy (app_wdf_rdy),
|
||||
.app_sr_req (1'b0),
|
||||
.app_ref_req (1'b0),
|
||||
.app_zq_req (1'b0),
|
||||
.app_sr_active (app_sr_active),
|
||||
.app_ref_ack (app_ref_ack),
|
||||
.app_zq_ack (app_zq_ack),
|
||||
.ui_clk (ui_clk),
|
||||
.ui_clk_sync_rst (ui_rst_sync),
|
||||
|
||||
// System Clock Ports
|
||||
.sys_clk_i (dram_front_clk),
|
||||
// Reference Clock Ports
|
||||
.clk_ref_i (dram_refclk),
|
||||
.device_temp (device_temp),
|
||||
.init_calib_complete (init_calib_complete),
|
||||
.sys_rst (rst_n)
|
||||
);
|
||||
|
||||
// reg [DRAM_DATA_WIDTH-1:0] read_cache;
|
||||
// reg [ADDR_WIDTH-1:0] cached_addr;
|
||||
// wire cache_hit = cached_addr == mem_addr;
|
||||
// wire [DRAM_DATA_WIDTH-1:0] read_data_wrapper = cache_hit ? read_cache : app_rd_data;
|
||||
|
||||
reg [WIDTH-1:0] read_buf;
|
||||
reg read_inprogress = 0;
|
||||
|
||||
assign app_rd_data_end = 1'b1;
|
||||
//assign app_wdf_mask = 16'b1111111111111100;
|
||||
|
||||
// addresses on the memory interface are aligned to 16 bytes
|
||||
// and 28 bits wide (=256MB)
|
||||
assign app_addr = { mem_addr[DRAM_ADDR_WIDTH:4], 4'b0000 };
|
||||
//assign app_addr = { 28'b0 };
|
||||
|
||||
// select a word from the 128 bits transferred by the dram controller
|
||||
// according to the lower bits of the address (ignoring bits 1:0)
|
||||
wire [WIDTH-1:0] read_word;
|
||||
wire [1:0] word_sel = mem_addr[3:2];
|
||||
|
||||
assign read_word = word_sel == 3'b11 ? app_rd_data[31:0] :
|
||||
word_sel == 3'b10 ? app_rd_data[63:32] :
|
||||
word_sel == 3'b01 ? app_rd_data[95:64] :
|
||||
app_rd_data[127:96];
|
||||
|
||||
assign mem_read_data = app_rd_data_valid ? read_word : read_buf;
|
||||
|
||||
// set the write mask according to the lower bits of the address
|
||||
// (ignoring bit 0)
|
||||
assign app_wdf_mask = word_sel == 3'b11 ? 16'b1111111111110000 :
|
||||
word_sel == 3'b10 ? 16'b1111111100001111 :
|
||||
word_sel == 3'b01 ? 16'b1111000011111111 :
|
||||
16'b0000111111111111 ;
|
||||
|
||||
wire write_ready = mem_write_enable & app_wdf_rdy & app_rdy;
|
||||
assign app_wdf_wren = mem_write_enable & write_ready;
|
||||
assign app_wdf_end = mem_write_enable & write_ready;
|
||||
assign app_wdf_data = { {4{mem_write_data}} };
|
||||
|
||||
assign mem_wait = (mem_read_enable & ~read_inprogress) |
|
||||
(mem_write_enable & (~app_wdf_rdy | ~app_rdy)) |
|
||||
(read_inprogress & ~app_rd_data_valid);
|
||||
|
||||
assign app_en = (mem_read_enable & ~read_inprogress) |
|
||||
(mem_write_enable & write_ready);
|
||||
assign app_cmd = mem_read_enable ? CMD_READ : CMD_WRITE;
|
||||
|
||||
always @(posedge dram_front_clk)
|
||||
begin
|
||||
if(mem_read_enable & ~read_inprogress & app_rdy)
|
||||
read_inprogress <= 1;
|
||||
if(read_inprogress & app_rd_data_valid)
|
||||
read_inprogress <= 0;
|
||||
if(mem_read_enable & app_rd_data_valid)
|
||||
read_buf <= mem_read_data;
|
||||
end
|
||||
endmodule
|
||||
53
rtl/src/fifo.v
Normal file
53
rtl/src/fifo.v
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
`timescale 1ns / 1ps
|
||||
|
||||
// a simple fifo
|
||||
module fifo #(parameter DATA_WIDTH = 8, ADDR_WIDTH = 4)(
|
||||
input wire clk,
|
||||
input wire reset,
|
||||
input wire wr_en,
|
||||
input wire rd_en,
|
||||
input wire [DATA_WIDTH-1:0] wr_data,
|
||||
output wire [DATA_WIDTH-1:0] rd_data,
|
||||
output wire wr_full,
|
||||
output wire rd_empty
|
||||
);
|
||||
|
||||
reg [DATA_WIDTH-1:0] mem [0:2**ADDR_WIDTH-1];
|
||||
reg [ADDR_WIDTH:0] head_x = 0; // head and tail have one extra bit
|
||||
reg [ADDR_WIDTH:0] tail_x = 0; // for detecting overflows
|
||||
wire [ADDR_WIDTH-1:0] head = head_x[ADDR_WIDTH-1:0];
|
||||
wire [ADDR_WIDTH-1:0] tail = tail_x[ADDR_WIDTH-1:0];
|
||||
|
||||
assign rd_data = mem[tail];
|
||||
// the fifo is full when head and tail pointer are the same
|
||||
// and the extra bits differ (a wraparound occured)
|
||||
assign wr_full = (head == tail) && (head_x[ADDR_WIDTH] != tail_x[ADDR_WIDTH]);
|
||||
// the fifo is empty when head and tail pointer are the same
|
||||
// and the extra bits are the same (no wraparound)
|
||||
assign rd_empty = (head == tail) && (head_x[ADDR_WIDTH] == tail_x[ADDR_WIDTH]);
|
||||
|
||||
// Writing to FIFO
|
||||
always @(posedge clk) begin
|
||||
if (reset)
|
||||
head_x <= 0;
|
||||
else if (wr_en)
|
||||
begin
|
||||
mem[head] <= wr_data;
|
||||
// move head, possible wraparound
|
||||
head_x <= head_x + 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
// Reading from FIFO
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if (reset)
|
||||
tail_x <= 0;
|
||||
else if (rd_en)
|
||||
begin
|
||||
// rd_data always has current tail data
|
||||
// move tail, possible wraparound
|
||||
tail_x <= tail_x + 1'b1;
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
116
rtl/src/fifo_testbench.v
Normal file
116
rtl/src/fifo_testbench.v
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
`timescale 1ns / 1ns
|
||||
`default_nettype none
|
||||
|
||||
module fifo_testbench();
|
||||
// Test signals
|
||||
reg clk = 0;
|
||||
reg reset = 0;
|
||||
reg wr_en = 0;
|
||||
reg rd_en = 0;
|
||||
reg [7:0] wr_data = 0;
|
||||
wire [7:0] rd_data;
|
||||
wire wr_full;
|
||||
wire rd_empty;
|
||||
|
||||
parameter CLOCK_NS = 10;
|
||||
|
||||
// Unit Under Test
|
||||
fifo #(
|
||||
.DATA_WIDTH(8),
|
||||
.ADDR_WIDTH(4)
|
||||
) UUT (
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.wr_en(wr_en),
|
||||
.rd_en(rd_en),
|
||||
.rd_data(rd_data),
|
||||
.wr_data(wr_data),
|
||||
.wr_full(wr_full),
|
||||
.rd_empty(rd_empty)
|
||||
);
|
||||
|
||||
// testbench clock
|
||||
always
|
||||
#(CLOCK_NS/2) clk <= ~clk;
|
||||
|
||||
initial
|
||||
begin
|
||||
// issue reset
|
||||
reset = 1'b1;
|
||||
#10
|
||||
reset = 1'b0;
|
||||
#10
|
||||
|
||||
// Write two bytes
|
||||
wr_data <= 8'hAB;
|
||||
wr_en <= 1'b1;
|
||||
#10;
|
||||
wr_data <= 8'hCD;
|
||||
wr_en <= 1'b1;
|
||||
#10;
|
||||
wr_en <= 1'b0;
|
||||
#10
|
||||
|
||||
// read fifo tail
|
||||
if (rd_data == 8'hAB)
|
||||
$display("Pass - Byte 1");
|
||||
else
|
||||
$display("Failed - Byte 2");
|
||||
|
||||
// read/remove byte from tail
|
||||
rd_en <= 1'b1;
|
||||
#10
|
||||
// check next byte
|
||||
if (rd_data == 8'hCD)
|
||||
$display("Pass - Byte 2");
|
||||
else
|
||||
$display("Failed - Byte 2");
|
||||
|
||||
// remove 2nd byte
|
||||
rd_en <= 1'b1;
|
||||
#10
|
||||
|
||||
rd_en <= 1'b0;
|
||||
#10
|
||||
|
||||
// Write until full
|
||||
rd_en <= 1'b0;
|
||||
wr_en <= 1'b0;
|
||||
|
||||
for (integer i = 0; i < 16; i = i + 1) begin
|
||||
wr_data <= i;
|
||||
wr_en <= 1'b1;
|
||||
#10;
|
||||
end
|
||||
wr_en <= 1'b0;
|
||||
|
||||
if (wr_full)
|
||||
$display("Pass - Fifo full");
|
||||
else
|
||||
$display("Failed - Fifo full");
|
||||
|
||||
|
||||
// read until empty
|
||||
rd_en <= 1'b0;
|
||||
wr_en <= 1'b0;
|
||||
|
||||
for (integer i = 0; i < 16; i = i + 1) begin
|
||||
rd_en <= 1'b1;
|
||||
#10;
|
||||
end
|
||||
rd_en <= 1'b0;
|
||||
|
||||
if (rd_empty)
|
||||
$display("Pass - Fifo empty");
|
||||
else
|
||||
$display("Failed - Fifo empty");
|
||||
$finish();
|
||||
end
|
||||
|
||||
initial
|
||||
begin
|
||||
// Required to dump signals
|
||||
$dumpfile("fifo_tb_dump.vcd");
|
||||
$dumpvars(0);
|
||||
end
|
||||
endmodule
|
||||
73
rtl/src/irqctrl.v
Normal file
73
rtl/src/irqctrl.v
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
`timescale 1ns / 1ps
|
||||
|
||||
module irqctrl #(IRQ_LINES = 2, IRQ_DELAY_WIDTH = 4) (
|
||||
input wire clk,
|
||||
input wire [IRQ_LINES-1:0] irq_in,
|
||||
input wire cs,
|
||||
input wire wr_en,
|
||||
input wire irq_wr_seten,
|
||||
output wire [IRQ_LINES-1:0] rd_data,
|
||||
output wire irq_out
|
||||
);
|
||||
|
||||
reg [IRQ_LINES-1:0] irq_status; // a 1 bit here means we have seen an interrupt on that line
|
||||
reg [IRQ_LINES-1:0] irq_mask; // a bit in irq_status is only set if the corresponding bit in irq_mask is not set
|
||||
// irq_mask is set from irq_status when an interrupt occurs (ie irq_out is set)
|
||||
|
||||
reg irq_enabled; // globally enable/disable irq_out
|
||||
reg [IRQ_DELAY_WIDTH-1:0] irq_delay; // counter to delay irq_out for a few cycles
|
||||
reg irq_delaying; // delay is active
|
||||
|
||||
wire irq_pending = (irq_status != 0);
|
||||
|
||||
assign rd_data = irq_mask;
|
||||
assign irq_out = irq_enabled && irq_pending && !irq_delaying;
|
||||
|
||||
// irq_status and irq_pending flags
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if(irq_out) // when an interrupt is being signaled to the cpu,
|
||||
irq_status <= 0; // clear irq status, status will be copied to irq_mask (see below)
|
||||
else
|
||||
if(irq_in != 0)
|
||||
irq_status <= irq_status | (irq_in & ~irq_mask); // add active irq to irq_status
|
||||
end
|
||||
|
||||
// irq mask
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if (cs && wr_en && irq_wr_seten) // when enabling interrupts, clear mask
|
||||
irq_mask <= 0;
|
||||
else
|
||||
if(irq_out) // when signalling an interrupt, set mask from status
|
||||
irq_mask <= irq_status;
|
||||
end
|
||||
|
||||
// manage irq_enabled and irq_delay/irq_delaying
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if(cs && wr_en) // when writing to control register
|
||||
begin
|
||||
if(irq_wr_seten) // if wr_seten flag is set, enable interrupts and start delay
|
||||
begin
|
||||
irq_enabled <= 1;
|
||||
irq_delaying <= 1;
|
||||
irq_delay <= 1;
|
||||
end
|
||||
else
|
||||
irq_enabled <= 0; // else disable interrupts
|
||||
end
|
||||
else if(irq_out) irq_enabled <= 0; // after sending interrupt to cpu, disable further interrupts
|
||||
|
||||
if(irq_delaying) // the delay gives the CPU a chance to return from an interrupt handler
|
||||
begin // if an interrupt is triggered again right after re-enabling interrupts
|
||||
if(irq_delay==0)
|
||||
begin
|
||||
irq_delay <= 1;
|
||||
irq_delaying <= 0;
|
||||
end
|
||||
else
|
||||
irq_delay <= irq_delay + 1;
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
161
rtl/src/mem.v
Normal file
161
rtl/src/mem.v
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
`timescale 1ns / 1ps
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// Company:
|
||||
// Engineer:
|
||||
//
|
||||
// Create Date: 05.01.2021 21:53:41
|
||||
// Design Name:
|
||||
// Module Name: mem
|
||||
// Project Name:
|
||||
// Target Devices:
|
||||
// Tool Versions:
|
||||
// Description:
|
||||
//
|
||||
// Dependencies:
|
||||
//
|
||||
// Revision:
|
||||
// Revision 0.01 - File Created
|
||||
// Additional Comments:
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// 32 bit wide rom with byte addressing (address bits 1-0 are ignored)
|
||||
module rom32 #(parameter ADDR_WIDTH = 11, DATA_WIDTH = 32)
|
||||
(
|
||||
input wire clk,
|
||||
input wire [ADDR_WIDTH-1:0] addr,
|
||||
output reg [DATA_WIDTH-1:0] data_out,
|
||||
input wire read_enable
|
||||
);
|
||||
|
||||
wire [ADDR_WIDTH-2:0] internal_addr = addr[ADDR_WIDTH-1:2]; // -> ignore bit 0
|
||||
reg [DATA_WIDTH-1:0] rom [0:(2**(ADDR_WIDTH-2))-1];
|
||||
|
||||
initial begin
|
||||
$readmemb("C:\\Users\\sebastian\\develop\\fpga\\vdumbcpu\\rom.mem", rom);
|
||||
end
|
||||
|
||||
always @(posedge clk) data_out <= rom[internal_addr];
|
||||
|
||||
endmodule
|
||||
|
||||
module ram32 #(parameter ADDR_WIDTH = 16, DATA_WIDTH = 32)
|
||||
|
||||
(
|
||||
input wire clk,
|
||||
input wire [ADDR_WIDTH-1:0] addr,
|
||||
output reg [DATA_WIDTH-1:0] data_out,
|
||||
input wire read_enable,
|
||||
input wire [DATA_WIDTH-1:0] data_in,
|
||||
input wire write_enable
|
||||
);
|
||||
|
||||
reg [DATA_WIDTH-1:0] ram [0:(2**(ADDR_WIDTH-2))-1]; // 32bit words with byte addressing
|
||||
wire [ADDR_WIDTH-2:0] internal_addr = addr[ADDR_WIDTH-1:2]; // -> ignore bit 1-0
|
||||
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if(read_enable)
|
||||
data_out <= ram[internal_addr];
|
||||
if(write_enable)
|
||||
ram[internal_addr] <= data_in;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module mem #(parameter ADDR_WIDTH = 32,
|
||||
parameter DATA_WIDTH = 32)
|
||||
(
|
||||
input wire clk, rst_n,
|
||||
input wire [ADDR_WIDTH-1:0] addr,
|
||||
output wire [DATA_WIDTH-1:0] data_out,
|
||||
input wire read_enable,
|
||||
input wire [DATA_WIDTH-1:0] data_in,
|
||||
input wire write_enable,
|
||||
output wire io_enable,
|
||||
input wire [DATA_WIDTH-1:0] io_rd_data,
|
||||
output wire mem_wait,
|
||||
|
||||
output wire [ADDR_WIDTH-1:0] dram_addr,
|
||||
input wire [DATA_WIDTH-1:0] dram_read_data,
|
||||
output wire [DATA_WIDTH-1:0] dram_write_data,
|
||||
output wire dram_read_enable,
|
||||
output wire dram_write_enable,
|
||||
input wire dram_wait
|
||||
);
|
||||
|
||||
wire [DATA_WIDTH-1:0] ram_out, rom_out, dram_out;
|
||||
|
||||
// address map:
|
||||
// ROM $0000 - $07FF 2K
|
||||
// IO $0800 - $0FFF 2K
|
||||
// RAM1 $1000 - $FFFF 60K
|
||||
// RAM2 $10000 - $FFFFFFFF ~4GB
|
||||
|
||||
wire ram_cs = addr[ADDR_WIDTH-1:12] != { {(ADDR_WIDTH-12){1'b0}}};
|
||||
wire ram1_cs = ram_cs && (addr[ADDR_WIDTH-1:16] == { {(ADDR_WIDTH-16){1'b0}}});
|
||||
wire ram2_cs = ram_cs && !ram1_cs;
|
||||
wire rom_cs = !ram_cs && addr[11] == 1'b0;
|
||||
wire io_cs = !ram_cs && addr[11] == 1'b1;
|
||||
|
||||
assign io_enable = io_cs;
|
||||
|
||||
wire ram_read = ram1_cs && read_enable;
|
||||
wire ram_write = ram1_cs && write_enable;
|
||||
wire rom_read = rom_cs && read_enable;
|
||||
|
||||
reg [DATA_WIDTH-1:0] data_buf;
|
||||
|
||||
localparam SEL_RAM1 = 0;
|
||||
localparam SEL_RAM2 = 1;
|
||||
localparam SEL_ROM = 2;
|
||||
localparam SEL_IO = 3;
|
||||
localparam SEL_ERR = 4;
|
||||
|
||||
reg [1:0] out_sel;
|
||||
|
||||
// test
|
||||
reg [1:0] wait_state;
|
||||
|
||||
ram32 #(.ADDR_WIDTH(16)) ram0 // 64KB RAM
|
||||
(
|
||||
.clk(clk),
|
||||
.addr(addr[15:0]),
|
||||
.data_out(ram_out),
|
||||
.read_enable(ram_read),
|
||||
.data_in(data_in),
|
||||
.write_enable(ram_write)
|
||||
);
|
||||
|
||||
rom32 #(.ADDR_WIDTH(11)) rom0 // 2KB ROM
|
||||
(
|
||||
.clk(clk),
|
||||
.addr(addr[10:0]),
|
||||
.data_out(rom_out),
|
||||
.read_enable(rom_read)
|
||||
);
|
||||
|
||||
assign dram_out = dram_read_data;
|
||||
assign dram_addr = addr;
|
||||
assign dram_write_data = data_in;
|
||||
assign dram_read_enable = ram2_cs & read_enable;
|
||||
assign dram_write_enable = ram2_cs & write_enable;
|
||||
|
||||
assign data_out = (out_sel == SEL_RAM1 ) ? ram_out :
|
||||
(out_sel == SEL_RAM2 ) ? dram_out :
|
||||
(out_sel == SEL_ROM ) ? rom_out :
|
||||
(out_sel == SEL_IO ) ? io_rd_data :
|
||||
data_buf;
|
||||
|
||||
assign mem_wait = ram2_cs && dram_wait;
|
||||
|
||||
always @(posedge clk)
|
||||
begin
|
||||
data_buf <= data_out;
|
||||
if(read_enable) out_sel <=
|
||||
ram1_cs ? SEL_RAM1 :
|
||||
ram2_cs ? SEL_RAM2:
|
||||
rom_cs ? SEL_ROM :
|
||||
io_cs ? SEL_IO :
|
||||
SEL_ERR;
|
||||
end
|
||||
endmodule
|
||||
28
rtl/src/palette.v
Normal file
28
rtl/src/palette.v
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
`timescale 1ns / 1ps
|
||||
// taken from https://danstrother.com/2010/09/11/inferring-rams-in-fpgas/
|
||||
// modified for one read/write-port and one read-only-port,
|
||||
/// A parameterized, inferable, true dual-port, dual-clock block RAM in Verilog.
|
||||
|
||||
module palette #(
|
||||
parameter SLOTS_WIDTH = 4, COLOR_WIDTH = 12
|
||||
) (
|
||||
input wire wr_clk,
|
||||
input wire rd_clk,
|
||||
input wire wr_en,
|
||||
input wire [SLOTS_WIDTH-1:0] wr_slot,
|
||||
input wire [COLOR_WIDTH-1:0] wr_data,
|
||||
input wire [SLOTS_WIDTH-1:0] rd_slot,
|
||||
output wire [COLOR_WIDTH-1:0] rd_data
|
||||
);
|
||||
|
||||
// Shared memory
|
||||
reg [COLOR_WIDTH-1:0] colors [(2**SLOTS_WIDTH)-1:0];
|
||||
|
||||
assign rd_data = colors[rd_slot];
|
||||
|
||||
always @(posedge wr_clk) begin
|
||||
if(wr_en) colors[wr_slot] <= wr_data;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
372
rtl/src/sdspi.v
Normal file
372
rtl/src/sdspi.v
Normal file
|
|
@ -0,0 +1,372 @@
|
|||
`timescale 1ns / 1ps
|
||||
|
||||
|
||||
// Every spi_clk_div cpu clock cycles the spi clock line is inverted.
|
||||
// So a spi clock cycle is 2*spi_clk_div cpu clock cycles.
|
||||
// spi_clk_count counts the cpu cycles from spi_clk_div down to zero.
|
||||
// The resulting spi clock frequency is:
|
||||
// (cpu clock freq) / ((sclk_count + 1) * 2)
|
||||
// So for a 83.33 MHz cpu clock, we get
|
||||
// spi_clk_div = 10: 83.333 / 22 = 3.788 MHz
|
||||
// spi_clk_div = 124: 83.333 / 250 = 333.33 KHz
|
||||
|
||||
|
||||
module sdspi(
|
||||
input wire clk, // bus clock
|
||||
input wire reset,
|
||||
|
||||
input wire[7:0] tx_data, // data to transmit
|
||||
output wire[7:0] rx_data, // data received
|
||||
output wire tx_ready, // ready to write a data byte
|
||||
output wire tx_empty, // transmitter fifo is empty
|
||||
output wire rx_avail, // a byte has been received
|
||||
output wire rx_ovr, // receiver overrun
|
||||
input wire tx_write, // write strobe
|
||||
input wire rx_read, // read strobe (clears rx_ovr)
|
||||
|
||||
output wire card_detect, // true is card is present
|
||||
output wire card_changed, // card_detect signal has changed
|
||||
output wire card_busy, // card is busy (MISO/DO is 0)
|
||||
|
||||
input wire ctrl_write, // set the following flags
|
||||
input wire rx_filter_en, // set to discard received $FF bytes
|
||||
input wire txrx_en, // enable transmitter and receiver
|
||||
input wire spiclk_f_en, // enable spi clock without cs
|
||||
input wire spiclk_div_wr, // set clock divider via tx_data
|
||||
|
||||
// PMOD connections
|
||||
output wire sd_cs_n,
|
||||
output reg sd_mosi,
|
||||
input wire sd_miso,
|
||||
output wire sd_sck,
|
||||
input wire sd_cd
|
||||
);
|
||||
|
||||
localparam CLKPHASE_0A = 2'b00;
|
||||
localparam CLKPHASE_0B = 2'b01;
|
||||
localparam CLKPHASE_1A = 2'b10;
|
||||
localparam CLKPHASE_1B = 2'b11;
|
||||
reg [1:0] clk_phase;
|
||||
|
||||
reg xcvr_on; // if turned off, the rest of the current byte
|
||||
// will still transmitted, and until then "running"
|
||||
// will be 1
|
||||
(* KEEP *)
|
||||
reg running; // transmitting/receiving a byte (maybe a dummy byte)
|
||||
|
||||
(* KEEP *) reg [3:0] xcvr_bitcount; // number of bits left of the current byte
|
||||
|
||||
reg [7:0] tx_shifter;
|
||||
wire tx_fifo_wr_en;
|
||||
reg tx_fifo_rd_en;
|
||||
wire tx_fifo_full;
|
||||
wire tx_fifo_empty;
|
||||
wire [7:0] tx_fifo_out;
|
||||
|
||||
reg [7:0] rx_shifter;
|
||||
reg rx_filter;
|
||||
reg rx_fifo_wr_en;
|
||||
wire rx_fifo_rd_en;
|
||||
wire rx_fifo_full;
|
||||
wire rx_fifo_empty;
|
||||
wire [7:0] rx_fifo_out;
|
||||
|
||||
reg rx_bit_recvd; // this flag signals a received bit
|
||||
|
||||
reg rx_overrun; // byte received when rx fifo is full
|
||||
|
||||
reg c_changed;
|
||||
reg c_cs;
|
||||
|
||||
reg spi_clk; // the spi clock signal
|
||||
reg spi_clk_on; // enable clock, either via init mode or by xcvr_on
|
||||
reg spi_clk_f_on; // init clock mode, i.e. start clock but no tx/rx
|
||||
reg [6:0] spi_clk_count; // counting cpu clock ticks
|
||||
reg [6:0] spi_clk_div; // tick counter for spi clock phases
|
||||
wire spi_clk_count_z = (spi_clk_count == 7'b0);
|
||||
reg hphase_start; // start of a spi clock half-phase
|
||||
|
||||
assign tx_ready = !tx_fifo_full;
|
||||
assign tx_empty = tx_fifo_empty;
|
||||
assign rx_avail = !rx_fifo_empty;
|
||||
assign rx_ovr = rx_overrun;
|
||||
assign rx_data = rx_fifo_out;
|
||||
|
||||
assign card_busy = (sd_miso == 0);
|
||||
assign card_changed = c_changed;
|
||||
|
||||
assign sd_sck = spi_clk;
|
||||
assign sd_cs_n = ~c_cs;
|
||||
|
||||
assign card_detect = sd_cd;
|
||||
|
||||
fifo #(.ADDR_WIDTH(4)) tx_fifo(clk, reset,
|
||||
tx_fifo_wr_en, tx_fifo_rd_en,
|
||||
tx_data, tx_fifo_out,
|
||||
tx_fifo_full,
|
||||
tx_fifo_empty
|
||||
);
|
||||
|
||||
fifo #(.ADDR_WIDTH(8)) rx_fifo(clk, reset,
|
||||
rx_fifo_wr_en, rx_fifo_rd_en,
|
||||
rx_shifter, rx_fifo_out,
|
||||
rx_fifo_full,
|
||||
rx_fifo_empty
|
||||
);
|
||||
|
||||
// spi clock
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if(reset)
|
||||
begin
|
||||
spi_clk <= 1; // CLK is high when inactive
|
||||
spi_clk_on <= 0;
|
||||
spi_clk_count <= 0;
|
||||
end
|
||||
else if(spi_clk_on)
|
||||
begin
|
||||
|
||||
// set spi_clk at start of every half-phase
|
||||
if(hphase_start)
|
||||
case(clk_phase)
|
||||
CLKPHASE_0A: spi_clk <= 1'b0;
|
||||
CLKPHASE_0B: spi_clk <= 1'b0;
|
||||
CLKPHASE_1A: spi_clk <= 1'b1;
|
||||
CLKPHASE_1B: spi_clk <= 1'b1;
|
||||
endcase
|
||||
|
||||
if(spi_clk_count_z)
|
||||
begin
|
||||
spi_clk_count <= spi_clk_div;
|
||||
clk_phase <= clk_phase + 2'b1;
|
||||
end
|
||||
else
|
||||
spi_clk_count <= spi_clk_count - 7'd1;
|
||||
end
|
||||
|
||||
// start the clock if needed
|
||||
if( (spi_clk_on == 0) && (running || spi_clk_f_on))
|
||||
begin
|
||||
spi_clk_on <= 1;
|
||||
spi_clk_count <= spi_clk_div;
|
||||
clk_phase <= CLKPHASE_1A;
|
||||
end
|
||||
|
||||
// turn off the clock if transceiver not running
|
||||
// and the force-clock-on flag is not set
|
||||
if( (spi_clk_on == 1) && (!running && !spi_clk_f_on))
|
||||
begin
|
||||
spi_clk_on <= 0;
|
||||
spi_clk <= 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
// half-phase-start flag trails spi_clk_count_z by one tick
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if(reset)
|
||||
hphase_start <= 0;
|
||||
else
|
||||
hphase_start <= spi_clk_on && spi_clk_count_z;
|
||||
end
|
||||
|
||||
// handle the force clock enable flag
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if (reset)
|
||||
spi_clk_f_on <= 0;
|
||||
else
|
||||
if (ctrl_write)
|
||||
spi_clk_f_on <= spiclk_f_en;
|
||||
end
|
||||
|
||||
// clock divider
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if (spiclk_div_wr) spi_clk_div <= tx_data[6:0];
|
||||
end
|
||||
|
||||
// card_changed flag
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if(sd_cd)
|
||||
c_changed <= 1;
|
||||
else if(ctrl_write || reset)
|
||||
c_changed <= 0;
|
||||
end
|
||||
|
||||
// cs signal
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if(hphase_start && clk_phase == CLKPHASE_0A || !running)
|
||||
c_cs <= running;
|
||||
end
|
||||
|
||||
// transmitter
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if(reset)
|
||||
begin
|
||||
// ???? we start the bitcount at 1 because we start
|
||||
// at the second clock phase where the bitcount
|
||||
// is decremented and the next byte gets loaded
|
||||
xcvr_bitcount <= 0;
|
||||
|
||||
tx_shifter <= 8'b1;
|
||||
xcvr_on <= 0;
|
||||
sd_mosi <= 1;
|
||||
tx_fifo_rd_en <= 0;
|
||||
end
|
||||
else
|
||||
begin
|
||||
// handle a control write to disable the transceiver
|
||||
if(ctrl_write && !txrx_en && xcvr_on)
|
||||
xcvr_on <= 0;
|
||||
// a byte might still be in transit, so
|
||||
// we do not disable the transceiver
|
||||
// immediately (see "handle running status" below)
|
||||
else
|
||||
// handle control write to enable the transceiver
|
||||
if(ctrl_write && txrx_en && !running)
|
||||
begin
|
||||
xcvr_on <= 1;
|
||||
xcvr_bitcount <= 0;
|
||||
// next clock phase must be 1B when starting the transceiver,
|
||||
// so that the first byte is loaded into the shifter then
|
||||
tx_shifter <= 8'b11111111;
|
||||
// in case the transceiver is enabled, but no data is in the fifo,
|
||||
// initialize the shifter with $FF
|
||||
end
|
||||
else
|
||||
// handle clock phases
|
||||
if (running)
|
||||
begin
|
||||
if(hphase_start)
|
||||
case(clk_phase)
|
||||
// set mosi signal at start of clock pulse
|
||||
CLKPHASE_0A: sd_mosi <= tx_shifter[7];
|
||||
CLKPHASE_0B: ;
|
||||
CLKPHASE_1A: begin // shift at rising clock
|
||||
tx_shifter <= tx_shifter << 1;
|
||||
xcvr_bitcount <= xcvr_bitcount - 1;
|
||||
end
|
||||
CLKPHASE_1B: begin // in the middle of the high clock pulse,
|
||||
// fetch the next byte if there are no bits
|
||||
// left in the shift register
|
||||
if (xcvr_bitcount == 0)
|
||||
begin
|
||||
if(!tx_fifo_empty)
|
||||
begin
|
||||
tx_shifter <= tx_fifo_out;
|
||||
tx_fifo_rd_en <= 1;
|
||||
end
|
||||
else
|
||||
tx_shifter <= 8'b11111111;
|
||||
xcvr_bitcount <= 8;
|
||||
end
|
||||
else
|
||||
tx_fifo_rd_en <= 0;
|
||||
end
|
||||
endcase
|
||||
else
|
||||
tx_fifo_rd_en <= 0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// handle data write
|
||||
assign tx_fifo_wr_en = tx_write && !tx_fifo_full;
|
||||
|
||||
// Enable fifo read signal if fifo is not empty.
|
||||
// The data at the fifo tail is always available at
|
||||
// rx_data, the read signal just moves the tail pointer
|
||||
// forward.
|
||||
assign rx_fifo_rd_en = rx_read && !rx_fifo_empty;
|
||||
|
||||
// receiver
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if(reset)
|
||||
begin
|
||||
rx_bit_recvd <= 0;
|
||||
rx_shifter <= 8'b11111111;
|
||||
rx_fifo_wr_en <= 0;
|
||||
rx_filter <= 0;
|
||||
rx_overrun <= 0;
|
||||
end
|
||||
else
|
||||
begin
|
||||
// handle a control write
|
||||
if(ctrl_write)
|
||||
begin
|
||||
rx_filter <= rx_filter_en;
|
||||
rx_overrun <= 0;
|
||||
if(txrx_en && !running)
|
||||
rx_shifter <= 8'b0;
|
||||
end
|
||||
|
||||
if (running && hphase_start)
|
||||
case(clk_phase)
|
||||
CLKPHASE_0A: ;
|
||||
CLKPHASE_0B: ;
|
||||
CLKPHASE_1A: ;
|
||||
CLKPHASE_1B: begin // in the middle of the high clock pulse,
|
||||
// sample MISO and put into shift register
|
||||
// and shift at the same time
|
||||
rx_shifter <= { rx_shifter[6:0],sd_miso};
|
||||
rx_bit_recvd <= 1;
|
||||
end
|
||||
endcase
|
||||
|
||||
if (rx_bit_recvd && !sd_cs_n && clk_phase == CLKPHASE_1B)
|
||||
begin
|
||||
rx_bit_recvd <= 0;
|
||||
|
||||
// if a complete byte was received, bitcount will be
|
||||
// 8 because the transmitter has already loaded the next byte
|
||||
// at this half-phase
|
||||
if (xcvr_bitcount == 8)
|
||||
begin
|
||||
// discard $FF bytes if filter is enabled
|
||||
if(!rx_filter || rx_shifter != 8'b11111111)
|
||||
begin
|
||||
if(rx_fifo_full) // discard received byte if fifo is full
|
||||
rx_overrun <= 1; // and set overrun flag
|
||||
else
|
||||
rx_fifo_wr_en <= 1; // otherwise, enable fifo write strobe,
|
||||
// fifo will take data from rx_shifter
|
||||
end
|
||||
|
||||
// turn off filter if a byte != $FF was received
|
||||
if (rx_filter && rx_shifter != 8'b11111111)
|
||||
rx_filter <= 0;
|
||||
end
|
||||
end
|
||||
else
|
||||
rx_fifo_wr_en <= 0;
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
// handle running status
|
||||
// (especially keep transmitter running when there are still bits left to be
|
||||
// transmitted)
|
||||
always@(posedge clk)
|
||||
begin
|
||||
if (reset)
|
||||
running <= 0;
|
||||
else
|
||||
begin
|
||||
// if we want to turn the transceiver on, set running flag
|
||||
if (!running && xcvr_on)
|
||||
running <= 1;
|
||||
|
||||
// when running and a byte has been transmitted,
|
||||
// check if we should turn the transceiver off
|
||||
if (running && hphase_start && xcvr_bitcount==0)
|
||||
if(clk_phase == CLKPHASE_1B)
|
||||
if (!xcvr_on)
|
||||
running <= 0;
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
199
rtl/src/sdspi_testbench.v
Normal file
199
rtl/src/sdspi_testbench.v
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
`timescale 1ns / 1ns
|
||||
`default_nettype none
|
||||
|
||||
module sdspi_testbench();
|
||||
parameter CLOCK_NS = 10;
|
||||
integer i,j;
|
||||
|
||||
reg[7:0] rx_testdata[0:3];
|
||||
reg[7:0] read_data;
|
||||
|
||||
// Test signals
|
||||
reg clk = 0;
|
||||
reg reset = 0;
|
||||
|
||||
reg[7:0] tx_data = 0;
|
||||
wire[7:0] rx_data;
|
||||
wire tx_ready;
|
||||
wire tx_empty;
|
||||
wire rx_avail;
|
||||
wire rx_ovr;
|
||||
reg tx_write = 0;
|
||||
reg rx_read = 0;
|
||||
|
||||
wire card_detect;
|
||||
wire card_changed;
|
||||
wire card_busy;
|
||||
|
||||
reg ctrl_write = 0;
|
||||
reg rx_filter_en = 0;
|
||||
reg txrx_en = 0;
|
||||
reg spiclk_f_en = 0;
|
||||
reg spiclk_div_wr = 0;
|
||||
|
||||
// PMOD connections
|
||||
wire sd_cs_n;
|
||||
wire sd_mosi;
|
||||
reg sd_miso = 1;
|
||||
wire sd_sck;
|
||||
reg sd_cd = 1;
|
||||
|
||||
// Unit Under Test
|
||||
sdspi UUT (
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.tx_data(tx_data),
|
||||
.rx_data(rx_data),
|
||||
.tx_ready(tx_ready),
|
||||
.tx_empty(tx_empty),
|
||||
.rx_avail(rx_avail),
|
||||
.rx_ovr(rx_ovr),
|
||||
.tx_write(tx_write),
|
||||
.rx_read(rx_read),
|
||||
.card_detect(card_detect),
|
||||
.card_changed(card_changed),
|
||||
.card_busy(card_busy),
|
||||
.ctrl_write(ctrl_write),
|
||||
.rx_filter_en(rx_filter_en),
|
||||
.txrx_en(txrx_en),
|
||||
.spiclk_f_en(spiclk_f_en),
|
||||
.spiclk_div_wr(spiclk_div_wr),
|
||||
.sd_cs_n(sd_cs_n),
|
||||
.sd_mosi(sd_mosi),
|
||||
.sd_miso(sd_miso),
|
||||
.sd_sck(sd_sck),
|
||||
.sd_cd(sd_cd)
|
||||
);
|
||||
|
||||
// testbench clock
|
||||
always
|
||||
#(CLOCK_NS/2) clk <= ~clk;
|
||||
|
||||
initial
|
||||
begin
|
||||
rx_testdata[0] <= 'hFF;
|
||||
rx_testdata[1] <= 'hFF;
|
||||
rx_testdata[2] <= 'hCA;
|
||||
rx_testdata[3] <= 'hFE;
|
||||
|
||||
// issue reset
|
||||
reset = 1'b1;
|
||||
#10
|
||||
reset = 1'b0;
|
||||
#10
|
||||
|
||||
// set clock divider
|
||||
tx_data <= 3;
|
||||
spiclk_div_wr <= 1'b1;
|
||||
#10
|
||||
spiclk_div_wr <= 1'b0;
|
||||
#10
|
||||
|
||||
// Card initialization phase,
|
||||
// cycle the clock at least 74 times
|
||||
// while cs is off and mosi is high .
|
||||
// we do 32 cycles
|
||||
spiclk_f_en <= 1'b1;
|
||||
ctrl_write <= 1'b1;
|
||||
#10
|
||||
spiclk_f_en <= 1'b0;
|
||||
ctrl_write <= 1'b0;
|
||||
#10
|
||||
for (i=0; i<32*16; i = i + 1)
|
||||
#10;
|
||||
spiclk_f_en <= 1'b0;
|
||||
ctrl_write <= 1'b1;
|
||||
#10
|
||||
ctrl_write <= 1'b0;
|
||||
#10
|
||||
for (i=0; i<32*16; i = i + 1)
|
||||
#10;
|
||||
|
||||
// Write two bytes
|
||||
tx_data <= 8'hAB;
|
||||
tx_write <= 1'b1;
|
||||
#10;
|
||||
tx_data <= 8'hCD;
|
||||
tx_write <= 1'b1;
|
||||
#10;
|
||||
tx_write <= 1'b0;
|
||||
#10
|
||||
|
||||
// start transceiver, enable rx_filter
|
||||
txrx_en <= 1'b1;
|
||||
rx_filter_en <= 1'b1;
|
||||
ctrl_write <= 1'b1;
|
||||
#10
|
||||
txrx_en <= 1'b0;
|
||||
rx_filter_en <= 1'b0;
|
||||
ctrl_write <= 1'b0;
|
||||
|
||||
for (i = 0; i < 2048; i = i + 1)
|
||||
begin
|
||||
if (!sd_cs_n && (i % 16)==15) sd_miso <= rx_testdata[(i/(16*8)) % 4][7 - (i/16 % 8)];
|
||||
#10;
|
||||
end
|
||||
tx_write <= 1'b0;
|
||||
#10
|
||||
|
||||
// read from rx fifo
|
||||
read_data <= rx_data;
|
||||
#10
|
||||
$display("read data 1: %02h", read_data);
|
||||
|
||||
|
||||
// strobe rx_read to go to next byte
|
||||
rx_read <= 1'b1;
|
||||
#10 // one cycle to transfer the data
|
||||
rx_read <= 1'b0;
|
||||
#10 // we need this extra cycle for the fifo tail to move
|
||||
|
||||
read_data <= rx_data;
|
||||
#10
|
||||
$display("read data 2: %02h", read_data);
|
||||
|
||||
// strobe rx_read to go to next byte
|
||||
rx_read <= 1'b1;
|
||||
#10
|
||||
rx_read <= 1'b0;
|
||||
#10 // we need this extra cycle for the fifo tail to move
|
||||
|
||||
read_data <= rx_data;
|
||||
#10
|
||||
$display("read data 3: %02h", read_data);
|
||||
|
||||
|
||||
// set flag to turn transceiver off
|
||||
txrx_en <= 1'b0;
|
||||
ctrl_write <= 1'b1;
|
||||
#10
|
||||
ctrl_write <= 1'b0;
|
||||
#10;
|
||||
|
||||
// wait for the transceiver to actually turn off
|
||||
for (i=0; i<32*16; i = i + 1)
|
||||
#10;
|
||||
|
||||
#10
|
||||
// clear rx fifo
|
||||
for(i=0; i<14; i = i + 1)
|
||||
begin
|
||||
$display("clear fifo data %02h", rx_data);
|
||||
rx_read <= 1'b1;
|
||||
#10
|
||||
rx_read <= 1'b0;
|
||||
#10
|
||||
#10
|
||||
#10; // simulate the four cycles of an instruction
|
||||
end
|
||||
|
||||
$finish();
|
||||
end
|
||||
|
||||
initial
|
||||
begin
|
||||
// Required to dump signals
|
||||
$dumpfile("sdspi_tb_dump.vcd");
|
||||
$dumpvars(0);
|
||||
end
|
||||
endmodule
|
||||
42
rtl/src/stack.v
Normal file
42
rtl/src/stack.v
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
`timescale 1ns / 1ps
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// Company:
|
||||
// Engineer:
|
||||
//
|
||||
// Create Date: 17.01.2021 20:59:29
|
||||
// Design Name:
|
||||
// Module Name: stack
|
||||
// Project Name:
|
||||
// Target Devices:
|
||||
// Tool Versions:
|
||||
// Description:
|
||||
//
|
||||
// Dependencies:
|
||||
//
|
||||
// Revision:
|
||||
// Revision 0.01 - File Created
|
||||
// Additional Comments:
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
module stack
|
||||
#(parameter ADDR_WIDTH=4, DATA_WIDTH=16)
|
||||
(
|
||||
input wire clk,
|
||||
input wire [ADDR_WIDTH-1:0] rd_addr,
|
||||
input wire [ADDR_WIDTH-1:0] wr_addr,
|
||||
input wire wr_enable,
|
||||
output wire [DATA_WIDTH-1:0] rd_data,
|
||||
input wire [DATA_WIDTH-1:0] wr_data
|
||||
);
|
||||
|
||||
reg [DATA_WIDTH-1:0] stack[0:2**ADDR_WIDTH-1];
|
||||
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if(wr_enable) stack[wr_addr] <= wr_data;
|
||||
end
|
||||
|
||||
assign rd_data = stack[rd_addr];
|
||||
endmodule
|
||||
427
rtl/src/stackcpu.v
Normal file
427
rtl/src/stackcpu.v
Normal file
|
|
@ -0,0 +1,427 @@
|
|||
`timescale 1ns / 1ps
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
module stackcpu #(parameter ADDR_WIDTH = 32, WIDTH = 32,
|
||||
WORDSIZE = 4, WORDSIZE_SHIFT = 2) (
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
input wire irq,
|
||||
|
||||
output reg [ADDR_WIDTH-1:0] addr,
|
||||
input wire [WIDTH-1:0] data_in,
|
||||
output wire read_enable,
|
||||
output wire [WIDTH-1:0] data_out,
|
||||
output wire write_enable,
|
||||
input wire mem_wait,
|
||||
|
||||
output wire led1,
|
||||
output wire led2,
|
||||
output wire led3,
|
||||
|
||||
output wire [WIDTH-1:0] debug_out1,
|
||||
output wire [WIDTH-1:0] debug_out2,
|
||||
output wire [WIDTH-1:0] debug_out3,
|
||||
output wire [WIDTH-1:0] debug_out4,
|
||||
output wire [WIDTH-1:0] debug_out5,
|
||||
output wire [WIDTH-1:0] debug_out6
|
||||
);
|
||||
|
||||
localparam EVAL_STACK_INDEX_WIDTH = 6;
|
||||
|
||||
wire reset = !rst;
|
||||
|
||||
(* KEEP *) reg [1:0] seq_state;
|
||||
localparam FETCH = 2'b00; localparam DECODE = 2'b01; localparam EXEC = 2'b10; localparam MEM = 2'b11;
|
||||
|
||||
(* KEEP*) reg [WIDTH-1:0] X, nX;
|
||||
wire [WIDTH-1:0] Y;
|
||||
(* KEEP *) reg [WIDTH-1:0] PC, nPC;
|
||||
reg [WIDTH-1:0] RP, nRP;
|
||||
reg [WIDTH-1:0] FP, BP;
|
||||
reg [WIDTH-1:0] IV,IR;
|
||||
|
||||
wire [WIDTH-1:0] pc_next_ins = PC + 2;
|
||||
|
||||
reg [EVAL_STACK_INDEX_WIDTH-1:0] ESP, nESP;
|
||||
reg stack_write;
|
||||
|
||||
reg irq_pending;
|
||||
|
||||
// eval stack
|
||||
stack #(.ADDR_WIDTH(EVAL_STACK_INDEX_WIDTH), .DATA_WIDTH(WIDTH)) estack (
|
||||
.clk(clk),
|
||||
.rd_addr(ESP),
|
||||
.wr_addr(nESP),
|
||||
.wr_enable(stack_write),
|
||||
.rd_data(Y),
|
||||
.wr_data(X)
|
||||
);
|
||||
|
||||
reg [15:0] ins;
|
||||
|
||||
wire [WIDTH-1:0] operand;
|
||||
|
||||
// decoded instructions
|
||||
wire ins_loadrel;
|
||||
wire ins_load;
|
||||
wire ins_loadc;
|
||||
wire ins_store;
|
||||
wire ins_aluop;
|
||||
wire ins_ext;
|
||||
wire ins_xfer;
|
||||
wire ins_branch;
|
||||
wire ins_cbranch;
|
||||
|
||||
// decoded extended instructions
|
||||
wire ins_mem, ins_loadi, ins_storei;
|
||||
wire ins_fpadj;
|
||||
wire ins_reg, ins_loadreg, ins_storereg;
|
||||
wire ins_reg_fp, ins_reg_bp, ins_reg_rp;
|
||||
wire ins_reg_iv, ins_reg_ir;
|
||||
wire ins_reg_esp;
|
||||
wire loadstore_base;
|
||||
wire cbranch_n;
|
||||
|
||||
wire xfer_x2p, xfer_r2p, xfer_p2r;
|
||||
wire [1:0] xfer_rs;
|
||||
|
||||
wire [3:0] aluop;
|
||||
wire [1:0] aluop_sd;
|
||||
wire aluop_x2y, aluop_ext;
|
||||
|
||||
wire cmp_i, cmp_e, cmp_l;
|
||||
|
||||
wire mem_read;
|
||||
wire mem_write;
|
||||
|
||||
wire x_is_zero;
|
||||
// wire [WIDTH-1:0] y_plus_operand = Y + operand;
|
||||
|
||||
wire x_equals_y = X == Y;
|
||||
wire y_lessthan_x = $signed(Y) < $signed(X);
|
||||
wire yx_unsigned_less = Y < X;
|
||||
|
||||
reg [WIDTH-1:0] mem_write_data;
|
||||
|
||||
wire mem_read_enable, mem_write_enable;
|
||||
|
||||
assign read_enable = mem_read_enable;
|
||||
assign data_out = mem_write_data;
|
||||
assign write_enable = mem_write_enable;
|
||||
|
||||
// debug output ------------------------------------------------------------------------------------
|
||||
assign led1 = reset;
|
||||
assign led2 = ins_loadc;
|
||||
assign led3 = ins_branch;
|
||||
// assign debug_out1 = { mem_read_enable, mem_write_enable, x_is_zero,
|
||||
// ins_branch, ins_aluop, y_lessthan_x, x_equals_y, {7{1'b0}}, seq_state};
|
||||
// assign debug_out2 = data_in;
|
||||
// assign debug_out3 = nX;
|
||||
// assign debug_out4 = nPC;
|
||||
// assign debug_out5 = ins;
|
||||
// assign debug_out6 = IV;
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
// instruction decoding
|
||||
assign ins_branch = (ins[15:13] == 3'b000);
|
||||
assign ins_aluop = (ins[15:13] == 3'b001);
|
||||
assign ins_store = (ins[15:13] == 3'b010);
|
||||
assign ins_xfer = (ins[15:13] == 3'b011);
|
||||
assign ins_load = (ins[15:13] == 3'b100);
|
||||
assign ins_cbranch = (ins[15:13] == 3'b101);
|
||||
assign ins_loadc = (ins[15:13] == 3'b110);
|
||||
assign ins_ext = (ins[15:13] == 3'b111);
|
||||
|
||||
// sub-decode LOAD/STORE
|
||||
assign loadstore_base = ins[0];
|
||||
|
||||
// sub-decode CBRANCH
|
||||
assign cbranch_n = ins[0];
|
||||
|
||||
// sub-decode XFER
|
||||
assign xfer_x2p = ins[0];
|
||||
assign xfer_r2p = ins[7];
|
||||
assign xfer_p2r = ins[6];
|
||||
assign xfer_rs = ins[9:8];
|
||||
|
||||
// sub-decode OP
|
||||
assign aluop = ins[12:9];
|
||||
assign aluop_x2y = ins[6];
|
||||
assign aluop_sd = ins[5:4];
|
||||
assign aluop_ext = ins[7];
|
||||
|
||||
// sub-decode OP.CMP
|
||||
assign cmp_i = ins[2];
|
||||
assign cmp_e = ins[1];
|
||||
assign cmp_l = ins[0];
|
||||
|
||||
assign x_is_zero = X == {WIDTH{1'b0}};
|
||||
|
||||
// decode extended instructions
|
||||
assign ins_reg = (ins_ext && ins[12:10] == 3'b000);
|
||||
assign ins_mem = (ins_ext && ins[12:10] == 3'b001);
|
||||
assign ins_loadi = (ins_mem && ins[9] == 1'b0);
|
||||
assign ins_storei = (ins_mem && ins[9] == 1'b1);
|
||||
assign ins_fpadj = (ins_ext && ins[12:10] == 3'b011);
|
||||
assign ins_loadrel= (ins_ext && ins[12:10] == 3'b101);
|
||||
|
||||
// sub-decode LOADREG/STOREREG
|
||||
assign ins_loadreg = (ins_reg && ins[9] == 1'b0);
|
||||
assign ins_storereg = (ins_reg && ins[9] == 1'b1);
|
||||
assign ins_reg_fp = (ins_reg && ins[3:0] == 4'b0000);
|
||||
assign ins_reg_bp = (ins_reg && ins[3:0] == 4'b0001);
|
||||
assign ins_reg_rp = (ins_reg && ins[3:0] == 4'b0010);
|
||||
assign ins_reg_iv = (ins_reg && ins[3:0] == 4'b0011);
|
||||
assign ins_reg_ir = (ins_reg && ins[3:0] == 4'b0100);
|
||||
assign ins_reg_esp = (ins_reg && ins[3:0] == 4'b0101);
|
||||
|
||||
assign mem_read = ins_loadi || ins_load || ins_loadrel || (ins_xfer && xfer_r2p);
|
||||
assign mem_write = ins_storei || ins_store || (ins_xfer && xfer_p2r);
|
||||
|
||||
assign mem_read_enable = (seq_state == FETCH) || (seq_state == EXEC && mem_read);
|
||||
assign mem_write_enable = (seq_state == MEM && mem_write);
|
||||
|
||||
initial
|
||||
begin
|
||||
PC <= 0; nPC <= 0; seq_state <= MEM;
|
||||
ESP <= -1; nESP <= -1;
|
||||
addr <= 0;
|
||||
FP <= 0; BP <= 0; RP <= 0; nRP <= 0;
|
||||
IV <= 0; IR <= 0;
|
||||
irq_pending <= 0;
|
||||
end
|
||||
|
||||
// instruction sequencer
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if(reset)
|
||||
seq_state <= MEM;
|
||||
else if(mem_wait == 1'b0)
|
||||
case(seq_state)
|
||||
FETCH: seq_state <= DECODE;
|
||||
DECODE: seq_state <= EXEC;
|
||||
EXEC: seq_state <= MEM;
|
||||
MEM: seq_state <= FETCH;
|
||||
default: seq_state <= FETCH;
|
||||
endcase
|
||||
end
|
||||
|
||||
// operand register
|
||||
assign operand =
|
||||
(ins_load || ins_store || ins_branch || ins_cbranch) ?
|
||||
{ {(WIDTH-13){ins[12]}}, ins[12:1], 1'b0 }
|
||||
: (ins_loadc) ? { {(WIDTH-13){ins[12]}}, ins[12:0] } // sign extend
|
||||
: (ins_aluop || ins_mem) ?
|
||||
{ {(WIDTH-4){1'b0}}, ins[3:0] }
|
||||
: (ins_loadrel) ? { {(WIDTH-10){1'b0}}, ins[9:0] }
|
||||
: (ins_fpadj) ? { {(WIDTH-10){ins[9]}}, ins[9:0] } // sign extend
|
||||
: { {WIDTH{1'b0}} };
|
||||
|
||||
// program counter
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if(reset) nPC <= 0;
|
||||
else
|
||||
case(seq_state)
|
||||
EXEC:
|
||||
if(ins_xfer && xfer_x2p) nPC <= X;
|
||||
else if(ins_branch || (ins_cbranch && (x_is_zero != cbranch_n))) nPC <= PC + operand;
|
||||
else nPC <= pc_next_ins;
|
||||
MEM:
|
||||
if(ins_xfer && xfer_r2p) nPC <= data_in;
|
||||
else if(irq_pending) nPC <= IV;
|
||||
endcase
|
||||
end
|
||||
|
||||
// return stack pointer
|
||||
always @*
|
||||
begin
|
||||
if(seq_state == EXEC || seq_state == DECODE || seq_state == MEM)
|
||||
begin
|
||||
if (ins_xfer) nRP <= RP +
|
||||
({ {(ADDR_WIDTH-3){xfer_rs[1]}},xfer_rs} << WORDSIZE_SHIFT);
|
||||
// sign extend xfer_rs and multiply by word size
|
||||
else if (ins_storereg && ins_reg_rp) nRP <= X;
|
||||
else nRP <= RP;
|
||||
end
|
||||
else nRP <= nRP;
|
||||
end
|
||||
|
||||
// instruction fetch
|
||||
// depending on bit 1 of the PC, read either the upper or lower half word as an instruction
|
||||
always @* if(seq_state == DECODE) ins <= PC[1] ? data_in[15:0] : data_in[31:16];
|
||||
|
||||
// RAM read/write
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if(reset)
|
||||
begin
|
||||
addr <= 0;
|
||||
mem_write_data <= 0;
|
||||
end
|
||||
else
|
||||
case(seq_state)
|
||||
DECODE:
|
||||
if(ins_load || ins_store) // read from address in BP/FP + offset
|
||||
addr <= operand + ( loadstore_base ? BP: FP);
|
||||
else if (ins_loadi) // read from address in X
|
||||
addr <= X;
|
||||
else if (ins_storei) // write to address in Y
|
||||
addr <= Y;
|
||||
else if (ins_loadrel) // read from address next to current instruction
|
||||
addr <= PC + operand;
|
||||
else if (ins_xfer && xfer_r2p) // read from return stack
|
||||
addr <= RP; // use the current RP
|
||||
else if (ins_xfer && xfer_p2r) // write to return stack
|
||||
addr <= nRP; // use the new RP
|
||||
EXEC:
|
||||
begin
|
||||
if (ins_store)
|
||||
mem_write_data <= X;
|
||||
else if (ins_storei)
|
||||
mem_write_data <= X;
|
||||
else if (ins_xfer && xfer_p2r)
|
||||
mem_write_data <= pc_next_ins;
|
||||
else
|
||||
mem_write_data <= 0;
|
||||
end
|
||||
MEM:
|
||||
if(!mem_wait) // do not change the address if mem_wait is active
|
||||
begin
|
||||
if(ins_xfer && xfer_r2p) addr <= data_in; // on RET take addr for next instruction from the data we just read from mem
|
||||
else addr <= irq_pending ? IV : nPC; // prepare fetch cycle
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
// X/ToS-Register
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if(reset) nX <= 0;
|
||||
else
|
||||
case(seq_state)
|
||||
// default: nX <= X;
|
||||
FETCH, DECODE:;
|
||||
EXEC:
|
||||
if(ins_loadc) nX <= operand;
|
||||
else if(ins_cbranch || ins_store || ins_storereg || (ins_xfer && xfer_x2p)) nX <= Y;
|
||||
else if(ins_storei) nX <= Y + operand;
|
||||
else if(ins_loadreg && ins_reg_fp) nX <= FP;
|
||||
else if(ins_loadreg && ins_reg_bp) nX <= BP;
|
||||
else if(ins_loadreg && ins_reg_rp) nX <= RP;
|
||||
else if(ins_loadreg && ins_reg_iv) nX <= IV;
|
||||
else if(ins_loadreg && ins_reg_ir) nX <= IR;
|
||||
else if(ins_loadreg && ins_reg_esp) nX <= ESP;
|
||||
else if(ins_aluop)
|
||||
begin
|
||||
case(aluop)
|
||||
4'b0000: nX = X + Y; // ADD
|
||||
4'b0001: nX = Y - X; // SUB
|
||||
4'b0010: nX = ~X; // NOT
|
||||
4'b0011: nX = X & Y; // AND
|
||||
4'b0100: nX = X | Y; // OR
|
||||
4'b0101: nX = X ^ Y; // XOR
|
||||
4'b0110: nX = // CMP
|
||||
cmp_i ^ ((cmp_e && x_equals_y) || (cmp_l && y_lessthan_x));
|
||||
4'b0111: nX = Y; // Y
|
||||
4'b1000: nX = aluop_ext ? X >>> 1 : X >> 1; // SHR
|
||||
4'b1001: nX = operand[1] ? X << 2 : X << 1; // SHL
|
||||
4'b1010: nX = X + operand; // INC
|
||||
4'b1011: nX = X - operand; // DEC
|
||||
4'b1100: nX = // CMPU
|
||||
cmp_i ^ ((cmp_e && x_equals_y) || (cmp_l && yx_unsigned_less));
|
||||
// 4'b1101: nX = X[7:0] << ((3 - Y[1:0]) << 3); // BPLC
|
||||
4'b1101: nX = Y[1:0] == 0 ? { X[7:0], 24'b0 } :
|
||||
Y[1:0] == 1 ? { 8'b0, X[7:0], 16'b0 } :
|
||||
Y[1:0] == 2 ? { 16'b0, X[7:0], 8'b0 } :
|
||||
{ 24'b0, X[7:0]}; // BPLC
|
||||
4'b1110: nX = { X[23:16], X[15:8], X[7:0], X[31:24] }; // BROT
|
||||
4'b1111: nX = { 24'b0, Y[1:0] == 0 ? X[31:24] : Y[1:0] == 1 ? X[23:16] :
|
||||
Y[1:0] == 2 ? X[15: 8] : X[7:0] }; // BSEL
|
||||
// 4'b1110: nX = X * Y; // MUL
|
||||
// 4'b1111: nX = X / Y; // DIV
|
||||
default: nX = X;
|
||||
endcase
|
||||
end
|
||||
MEM:
|
||||
if (ins_loadi || ins_load || ins_loadrel)
|
||||
nX = data_in;
|
||||
endcase
|
||||
end
|
||||
|
||||
// estack movement
|
||||
wire [EVAL_STACK_INDEX_WIDTH-1:0] delta =
|
||||
((ins_load || ins_loadc || ins_loadreg || ins_loadrel)) ? 1
|
||||
: ((ins_aluop || ins_loadi || ins_storei || ins_xfer)) ?
|
||||
{ {(EVAL_STACK_INDEX_WIDTH-2){aluop_sd[1]}},aluop_sd} // sign extend
|
||||
: ((ins_store || ins_cbranch || ins_xfer || ins_storereg)) ? -1
|
||||
: 0;
|
||||
|
||||
|
||||
always @*
|
||||
begin
|
||||
if(reset)
|
||||
nESP <= 0;
|
||||
else
|
||||
if(seq_state == EXEC)
|
||||
begin
|
||||
nESP = ESP + delta;
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge clk)
|
||||
begin
|
||||
// when to write (old) X back to stack (new Y)
|
||||
// stack write is a reg so it is 1 in the next cycle i.e. MEM state
|
||||
stack_write <= (seq_state == EXEC &&
|
||||
(ins_load || ins_loadc || ins_loadrel || ins_loadreg
|
||||
|| ((ins_loadi || ins_storei || ins_aluop) && aluop_x2y)));
|
||||
end
|
||||
|
||||
// FP register
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if(seq_state == EXEC)
|
||||
begin
|
||||
if(ins_fpadj) FP <= FP + operand;
|
||||
else if(ins_storereg && ins_reg_fp) FP <= X;
|
||||
end
|
||||
end
|
||||
|
||||
// BP register
|
||||
always @(posedge clk) if(seq_state == EXEC && ins_storereg && ins_reg_bp) BP <= X;
|
||||
|
||||
// IV register
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if(reset)
|
||||
IV <= 0;
|
||||
else if(seq_state == EXEC && ins_storereg && ins_reg_iv)
|
||||
IV <= X;
|
||||
end
|
||||
|
||||
// IR register
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if(seq_state == MEM && irq_pending) IR <= nPC; // use nPC as interrupt return addr
|
||||
end
|
||||
|
||||
// process irq
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if(seq_state == MEM && irq_pending && !(ins_xfer & xfer_r2p)) // in FETCH state, clear irq_pending.
|
||||
irq_pending <= 0;
|
||||
else
|
||||
irq_pending <= irq_pending || irq; // else set irq_pending when irq is high
|
||||
end
|
||||
|
||||
// advance CPU state
|
||||
always @ (posedge clk)
|
||||
begin
|
||||
if(reset)
|
||||
{ PC, X, ESP, RP } <= { {WIDTH{1'b0}}, {WIDTH{1'b0}}, {WIDTH{1'b0}}, {WIDTH{1'b0}} };
|
||||
else if(seq_state == FETCH)
|
||||
{ PC, X, ESP, RP } <= { nPC, nX, nESP, nRP};
|
||||
end
|
||||
endmodule
|
||||
54
rtl/src/testbench.v
Normal file
54
rtl/src/testbench.v
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
`timescale 1ns/1ps
|
||||
`default_nettype none
|
||||
|
||||
module testbench();
|
||||
reg clk;
|
||||
reg rst_n;
|
||||
wire btn0;
|
||||
wire sw0;
|
||||
wire sw1;
|
||||
wire led0;
|
||||
wire led1;
|
||||
wire led2;
|
||||
wire led3;
|
||||
wire uart_txd_in;
|
||||
wire uart_rxd_out;
|
||||
|
||||
wire [15:0] ddr3_dq;
|
||||
wire [1:0] ddr3_dqs_n;
|
||||
wire [1:0] ddr3_dqs_p;
|
||||
wire [13:0] ddr3_addr;
|
||||
wire [2:0] ddr3_ba;
|
||||
wire ddr3_ras_n;
|
||||
wire ddr3_cas_n;
|
||||
wire ddr3_we_n;
|
||||
wire ddr3_reset_n;
|
||||
wire [0:0] ddr3_ck_p;
|
||||
wire [0:0] ddr3_ck_n;
|
||||
wire [0:0] ddr3_cke;
|
||||
wire [0:0] ddr3_cs_n;
|
||||
wire [1:0] ddr3_dm;
|
||||
wire [0:0] ddr3_odt;
|
||||
|
||||
integer t;
|
||||
|
||||
top top0(clk, rst_n, btn0, sw0,sw1, led0, led1, led2, led3, uart_txd_in, uart_rxd_out,
|
||||
ddr3_dq, ddr3_dqs_n, ddr3_dqs_p, ddr3_addr, ddr3_ba, ddr3_ras_n, ddr3_cas_n,
|
||||
ddr3_we_n, ddr3_reset_n, ddr3_ck_p, ddr3_ck_n, ddr3_cke, ddr3_cs_n, ddr3_dm, ddr3_odt);
|
||||
|
||||
initial begin
|
||||
clk = 1;
|
||||
t = 0;
|
||||
rst_n = 0;
|
||||
end
|
||||
|
||||
always #5.0 clk = ~clk;
|
||||
|
||||
always @(posedge clk) begin
|
||||
t <= t + 1;
|
||||
if (t == 2)
|
||||
rst_n = 1;
|
||||
if (t == 400)
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
288
rtl/src/top.v
Normal file
288
rtl/src/top.v
Normal file
|
|
@ -0,0 +1,288 @@
|
|||
`timescale 1ns / 1ps
|
||||
// either define clock as clk (100MHz on Arty)
|
||||
// or as clk_1hz for debugging
|
||||
|
||||
`define clock cpuclk
|
||||
`define clkfreq 83333333
|
||||
//`define clock clk
|
||||
//`define clkfreq 100000000
|
||||
//`define clock clk_1hz
|
||||
`define ENABLE_VGAFB
|
||||
`define ENABLE_MICROSD
|
||||
|
||||
module top(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
input wire btn0,
|
||||
input wire sw0,
|
||||
input wire sw1,
|
||||
output wire led0,
|
||||
output wire led1,
|
||||
output wire led2,
|
||||
output wire led3,
|
||||
input wire uart_txd_in,
|
||||
output wire uart_rxd_out,
|
||||
|
||||
// DDR3 SDRAM
|
||||
inout wire [15:0] ddr3_dq,
|
||||
inout wire [1:0] ddr3_dqs_n,
|
||||
inout wire [1:0] ddr3_dqs_p,
|
||||
|
||||
output wire [13:0] ddr3_addr,
|
||||
output wire [2:0] ddr3_ba,
|
||||
output wire ddr3_ras_n,
|
||||
output wire ddr3_cas_n,
|
||||
output wire ddr3_we_n,
|
||||
output wire ddr3_reset_n,
|
||||
output wire [0:0] ddr3_ck_p,
|
||||
output wire [0:0] ddr3_ck_n,
|
||||
output wire [0:0] ddr3_cke,
|
||||
output wire [0:0] ddr3_cs_n,
|
||||
output wire [1:0] ddr3_dm,
|
||||
output wire [0:0] ddr3_odt
|
||||
|
||||
`ifdef ENABLE_VGAFB
|
||||
,
|
||||
output wire [3:0] VGA_R,
|
||||
output wire [3:0] VGA_G,
|
||||
output wire [3:0] VGA_B,
|
||||
|
||||
output wire VGA_HS_O,
|
||||
output wire VGA_VS_O
|
||||
`endif
|
||||
|
||||
`ifdef ENABLE_MICROSD
|
||||
,
|
||||
output wire sd_cs_n,
|
||||
output wire sd_mosi,
|
||||
input wire sd_miso,
|
||||
output wire sd_sck,
|
||||
input wire sd_cd
|
||||
`endif
|
||||
);
|
||||
|
||||
reg clk_1hz;
|
||||
reg [31:0] counter;
|
||||
|
||||
localparam ADDR_WIDTH = 32, WIDTH = 32,
|
||||
ROMADDR_WIDTH = 11, IOADDR_WIDTH = 11, IOADDR_SEL = 4;
|
||||
|
||||
wire [ADDR_WIDTH-1:0] mem_addr;
|
||||
wire [WIDTH-1:0] mem_read_data;
|
||||
wire [WIDTH-1:0] mem_write_data;
|
||||
(* KEEP *) wire mem_wait;
|
||||
|
||||
(* KEEP *) wire mem_read_enable;
|
||||
(* KEEP *) wire mem_write_enable;
|
||||
(* KEEP *) wire io_enable;
|
||||
wire [WIDTH-1:0] io_rd_data;
|
||||
wire [IOADDR_SEL-1:0] io_slot = mem_addr[IOADDR_WIDTH-1:IOADDR_WIDTH-IOADDR_SEL];
|
||||
|
||||
wire irq;
|
||||
|
||||
// assign led0 = mem_wait;
|
||||
|
||||
wire [WIDTH-1:0] debug_data1, debug_data2,
|
||||
debug_data3, debug_data4,
|
||||
debug_data5, debug_data6;
|
||||
|
||||
assign led0 = debug_data6[0];
|
||||
|
||||
wire cpuclk, cpuclk_locked;
|
||||
wire dram_refclk200;
|
||||
wire pixclk;
|
||||
cpu_clkgen cpuclk_0(~rst, clk, cpuclk, dram_refclk200, pixclk, cpuclk_locked);
|
||||
|
||||
// DRAM --------------------------------------------------------------------------
|
||||
wire [ADDR_WIDTH-1:0] dram_addr;
|
||||
wire [WIDTH-1:0] dram_read_data, dram_write_data;
|
||||
wire dram_read_enable, dram_write_enable, dram_wait;
|
||||
|
||||
dram_bridge dram_bridge0 (dram_addr,
|
||||
dram_read_data, dram_write_data, dram_read_enable, dram_write_enable, dram_wait,
|
||||
rst, cpuclk, dram_refclk200,
|
||||
ddr3_dq, ddr3_dqs_n, ddr3_dqs_p, ddr3_addr,
|
||||
ddr3_ba, ddr3_ras_n, ddr3_cas_n, ddr3_we_n,
|
||||
ddr3_reset_n, ddr3_ck_p, ddr3_ck_n, ddr3_cke,
|
||||
ddr3_cs_n, ddr3_dm, ddr3_odt);
|
||||
|
||||
mem #(.ADDR_WIDTH(ADDR_WIDTH), .DATA_WIDTH(WIDTH)) mem0(
|
||||
.clk(`clock), .rst_n(rst), .addr(mem_addr),
|
||||
.data_out(mem_read_data), .read_enable(mem_read_enable),
|
||||
.data_in(mem_write_data), .write_enable(mem_write_enable),
|
||||
.io_enable(io_enable),
|
||||
.io_rd_data(io_rd_data),
|
||||
.mem_wait(mem_wait),
|
||||
.dram_addr(dram_addr),
|
||||
.dram_read_data(dram_read_data),
|
||||
.dram_write_data(dram_write_data),
|
||||
.dram_read_enable(dram_read_enable),
|
||||
.dram_write_enable(dram_write_enable),
|
||||
.dram_wait(dram_wait)
|
||||
);
|
||||
|
||||
`ifdef ENABLE_VGAFB
|
||||
localparam FB_ADDR_WIDTH = 14;
|
||||
wire [FB_ADDR_WIDTH-1:0] fb_rd_addr;
|
||||
wire [FB_ADDR_WIDTH-1:0] fb_wr_addr;
|
||||
wire [WIDTH-1:0] fb_rd_data;
|
||||
wire [WIDTH-1:0] fb_wr_data;
|
||||
wire fb_rd_en, fb_wr_en;
|
||||
|
||||
wire fb_cs_en = io_enable && (io_slot == 2);
|
||||
|
||||
assign fb_rd_en = fb_cs_en && mem_read_enable;
|
||||
assign fb_wr_en = fb_cs_en && mem_write_enable;
|
||||
assign fb_wr_data = mem_write_data;
|
||||
|
||||
vgafb vgafb0(`clock, pixclk, rst,
|
||||
mem_addr[3:0], fb_rd_data, fb_wr_data,
|
||||
fb_rd_en, fb_wr_en,
|
||||
VGA_HS_O, VGA_VS_O, VGA_R, VGA_G, VGA_B);
|
||||
`endif
|
||||
|
||||
// SPI SD card controller -------------------------------------------------------------------
|
||||
`ifdef ENABLE_MICROSD
|
||||
wire [7:0] spi_tx_data;
|
||||
(*KEEP*) wire [7:0] spi_rx_data;
|
||||
wire spi_tx_ready; // ready to transmit new data
|
||||
wire spi_tx_empty; // tx fifo is empty
|
||||
wire spi_rx_avail; // a byte has been received
|
||||
wire spi_rx_ovr; // receiver overrun
|
||||
wire spi_tx_write; // write strobe
|
||||
wire spi_rx_read; // read strobe (clears rx_avail)
|
||||
|
||||
wire spi_card_detect; // true is card is present
|
||||
wire spi_card_changed; // card_detect signal has changed
|
||||
wire spi_card_busy; // card is busy (MISO/DO is 0)
|
||||
|
||||
wire spi_ctrl_write; // set the following flags
|
||||
wire spi_rx_filter_en; // set to wait for start bit (1-to-0) when receiving
|
||||
wire spi_txrx_en; // enable transmitter and receiver
|
||||
wire spi_sclk_f_en; // enable spi clock without transceiver
|
||||
wire spi_sclk_div_wr; // set clock divider from tx_data
|
||||
|
||||
wire spi_cs; // cs signal for spi controller
|
||||
wire [WIDTH-1:0] spi_rd_data;
|
||||
|
||||
assign spi_cs = io_enable && (io_slot == 1);
|
||||
|
||||
// spi read data: [ 0,...,0,cd,cc,cb,tr,te,ra,ro,d,d,d,d,d,d,d,d ]
|
||||
// cd = card detect, cc = card changed, cb = card busy,
|
||||
// tr = transmitter ready, te = tx fifo empty,
|
||||
// ra = received byte available, ro = receive overrun, d = received byte
|
||||
assign spi_rd_data =
|
||||
{ {WIDTH-15{1'b0}}, spi_card_detect, spi_card_changed, spi_card_busy,
|
||||
spi_tx_ready, spi_tx_empty,
|
||||
spi_rx_avail, spi_rx_ovr, spi_rx_data };
|
||||
|
||||
// spi write data: [ 0,...,0,CW,CF,Cx,Cc,Cd,DR,DW,d,d,d,d,d,d,d,d ]
|
||||
// CW = control write, CF = enable receive filter, Cx = enable transceiver,
|
||||
// Cc = force spi clock on, Cd = write clock divider,
|
||||
// DR = read acknowledge, DW = data write, d = byte to be sent
|
||||
assign spi_ctrl_write = spi_cs && mem_write_enable && mem_write_data[14];
|
||||
assign spi_rx_filter_en = mem_write_data[13];
|
||||
assign spi_txrx_en = mem_write_data[12];
|
||||
assign spi_sclk_f_en = mem_write_data[11];
|
||||
assign spi_sclk_div_wr = spi_cs && mem_write_enable && mem_write_data[10];
|
||||
assign spi_rx_read = mem_write_data[9];
|
||||
assign spi_tx_write = spi_cs && mem_write_enable && mem_write_data[8];
|
||||
assign spi_tx_data = mem_write_data[7:0];
|
||||
|
||||
sdspi sdspi0(.clk(`clock), .reset(~rst),
|
||||
.tx_data(spi_tx_data), .rx_data(spi_rx_data),
|
||||
.tx_ready(spi_tx_ready), .tx_empty(spi_tx_empty),
|
||||
.rx_avail(spi_rx_avail), .rx_ovr(spi_rx_ovr),
|
||||
.tx_write(spi_tx_write), .rx_read(spi_rx_read),
|
||||
.card_detect(spi_card_detect), .card_changed(spi_card_changed), .card_busy(spi_card_busy),
|
||||
// ctrl_write is used with rx_filter_en, txrx_en and spiclk_f_en
|
||||
.ctrl_write(spi_ctrl_write),
|
||||
.rx_filter_en(spi_rx_filter_en), .txrx_en(spi_txrx_en), .spiclk_f_en(spi_sclk_f_en),
|
||||
//
|
||||
.spiclk_div_wr(spi_sclk_div_wr),
|
||||
.sd_cs_n(sd_cs_n),
|
||||
.sd_mosi(sd_mosi), .sd_miso(sd_miso), .sd_sck(sd_sck), .sd_cd(sd_cd));
|
||||
`endif
|
||||
|
||||
// UART -----------------------------------------------------------------------
|
||||
|
||||
// uart write data: [ 0, 0, 0, 0, 0, T, C, 0, c, c, c, c, c, c, c, c ]
|
||||
// T = transmit enable, C = receiver clear, c = 8-bit-character
|
||||
// uart read data: [ 0, 0, 0, 0, 0, 0, A, B, c, c, c, c, c, c, c, c ]
|
||||
// A = char available, B = tx busy, c = 8-bit-character
|
||||
wire uart_cs = io_enable && (io_slot == 0);
|
||||
wire uart_tx_en = uart_cs && mem_write_enable && mem_write_data[10];
|
||||
wire uart_rx_clear = uart_cs && mem_write_enable && mem_write_data[9];
|
||||
wire uart_rx_avail;
|
||||
wire uart_rx_busy, uart_tx_busy;
|
||||
wire uart_err;
|
||||
wire [7:0] uart_rx_data;
|
||||
wire [7:0] uart_tx_data;
|
||||
wire [31:0] uart_baud = 32'd115200;
|
||||
wire [WIDTH-1:0] uart_rd_data;
|
||||
|
||||
assign uart_tx_data = mem_write_data[7:0];
|
||||
assign uart_rd_data = { {WIDTH-10{1'b1}}, uart_rx_avail, uart_tx_busy, uart_rx_data };
|
||||
|
||||
reg timer_tick;
|
||||
reg[23:0] tick_count;
|
||||
wire [1:0] irq_in = { timer_tick, uart_rx_avail };
|
||||
wire [1:0] irqc_rd_data0;
|
||||
wire [WIDTH-1:0] irqc_rd_data = { tick_count, 6'b0, irqc_rd_data0 };
|
||||
wire irqc_seten = mem_write_data[7];
|
||||
wire irqc_cs = io_enable && (io_slot == 3);
|
||||
|
||||
assign io_rd_data = (io_slot == 0) ? uart_rd_data :
|
||||
`ifdef ENABLE_MICROSD
|
||||
(io_slot == 1) ? spi_rd_data :
|
||||
`endif
|
||||
`ifdef ENABLE_VGAFB
|
||||
(io_slot == 2) ? fb_rd_data :
|
||||
`endif
|
||||
(io_slot == 3) ? irqc_rd_data:
|
||||
|
||||
-1;
|
||||
|
||||
buart #(.CLKFREQ(`clkfreq)) uart0(`clock, rst,
|
||||
uart_baud,
|
||||
uart_txd_in, uart_rxd_out,
|
||||
uart_rx_clear, uart_tx_en,
|
||||
uart_rx_avail, uart_tx_busy,
|
||||
uart_tx_data, uart_rx_data);
|
||||
|
||||
// CPU -----------------------------------------------------------------
|
||||
stackcpu cpu0(.clk(`clock), .rst(rst), .irq(irq),
|
||||
.addr(mem_addr),
|
||||
.data_in(mem_read_data), .read_enable(mem_read_enable),
|
||||
.data_out(mem_write_data), .write_enable(mem_write_enable),
|
||||
.mem_wait(mem_wait),
|
||||
.led1(led1), .led2(led2), .led3(led3),
|
||||
.debug_out1(debug_data1),
|
||||
.debug_out2(debug_data2),
|
||||
.debug_out3(debug_data3),
|
||||
.debug_out4(debug_data4),
|
||||
.debug_out5(debug_data5),
|
||||
.debug_out6(debug_data6));
|
||||
|
||||
// Interrupt Controller
|
||||
irqctrl irqctrl0(`clock, irq_in, irqc_cs, mem_write_enable,
|
||||
irqc_seten, irqc_rd_data0,
|
||||
irq);
|
||||
|
||||
// count clock ticks
|
||||
// generate interrupt every 20nth of a second
|
||||
always @ (posedge `clock)
|
||||
begin
|
||||
counter <= counter + 1;
|
||||
if (counter >= (`clkfreq/20))
|
||||
begin
|
||||
counter <= 0;
|
||||
timer_tick <= 1;
|
||||
tick_count <= tick_count + 1'b1;
|
||||
end
|
||||
else
|
||||
begin
|
||||
timer_tick <= 0;
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
262
rtl/src/uart.v
Normal file
262
rtl/src/uart.v
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
/*
|
||||
Copyright (c) 2010-2020, James Bowman
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of swapforth nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
`default_nettype none
|
||||
//`define def_clkfreq 100000000
|
||||
`define def_clkfreq 83333333
|
||||
|
||||
module baudgen(
|
||||
input wire clk,
|
||||
input wire resetq,
|
||||
input wire [31:0] baud,
|
||||
input wire restart,
|
||||
output wire ser_clk);
|
||||
parameter CLKFREQ = `def_clkfreq;
|
||||
|
||||
wire [38:0] aclkfreq = CLKFREQ;
|
||||
reg [38:0] d;
|
||||
wire [38:0] dInc = d[38] ? ({4'd0, baud}) : (({4'd0, baud}) - aclkfreq);
|
||||
wire [38:0] dN = restart ? 0 : (d + dInc);
|
||||
wire fastclk = ~d[38];
|
||||
assign ser_clk = fastclk;
|
||||
|
||||
always @(negedge resetq or posedge clk)
|
||||
begin
|
||||
if (!resetq) begin
|
||||
d <= 0;
|
||||
end else begin
|
||||
d <= dN;
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
/*
|
||||
|
||||
-----+ +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+----
|
||||
| | | | | | | | | | | |
|
||||
|start| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |stop1|stop2|
|
||||
| | | | | | | | | | | ? |
|
||||
+-----+-----+-----+-----+-----+-----+-----+-----+-----+ +
|
||||
|
||||
*/
|
||||
|
||||
module uart(
|
||||
input wire clk, // System clock
|
||||
input wire resetq,
|
||||
|
||||
// Outputs
|
||||
output wire uart_busy, // High means UART is transmitting
|
||||
output reg uart_tx, // UART transmit wire
|
||||
// Inputs
|
||||
input wire [31:0] baud,
|
||||
input wire uart_wr_i, // Raise to transmit byte
|
||||
input wire [7:0] uart_dat_i // 8-bit data
|
||||
);
|
||||
parameter CLKFREQ = `def_clkfreq;
|
||||
|
||||
reg [3:0] bitcount;
|
||||
reg [8:0] shifter;
|
||||
|
||||
assign uart_busy = |bitcount;
|
||||
wire sending = |bitcount;
|
||||
|
||||
wire ser_clk;
|
||||
|
||||
wire starting = uart_wr_i & ~uart_busy;
|
||||
baudgen #(.CLKFREQ(CLKFREQ)) _baudgen(
|
||||
.clk(clk),
|
||||
.resetq(resetq),
|
||||
.baud(baud),
|
||||
.restart(1'b0),
|
||||
.ser_clk(ser_clk));
|
||||
|
||||
always @(negedge resetq or posedge clk)
|
||||
begin
|
||||
if (!resetq) begin
|
||||
uart_tx <= 1;
|
||||
bitcount <= 0;
|
||||
shifter <= 0;
|
||||
end else begin
|
||||
if (starting) begin
|
||||
shifter <= { uart_dat_i[7:0], 1'b0 };
|
||||
bitcount <= 1 + 8 + 1;
|
||||
end
|
||||
|
||||
if (sending & ser_clk) begin
|
||||
{ shifter, uart_tx } <= { 1'b1, shifter };
|
||||
bitcount <= bitcount - 4'd1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
module rxuart(
|
||||
input wire clk,
|
||||
input wire resetq,
|
||||
input wire [31:0] baud,
|
||||
input wire uart_rx, // UART recv wire
|
||||
input wire rd, // read strobe
|
||||
output wire valid, // has data
|
||||
output wire [7:0] data); // data
|
||||
parameter CLKFREQ = `def_clkfreq;
|
||||
|
||||
reg [4:0] bitcount;
|
||||
reg [7:0] shifter;
|
||||
|
||||
// On starting edge, wait 3 half-bits then sample, and sample every 2 bits thereafter
|
||||
|
||||
wire idle = &bitcount;
|
||||
wire sample;
|
||||
reg [2:0] hh = 3'b111;
|
||||
wire [2:0] hhN = {hh[1:0], uart_rx};
|
||||
wire startbit = idle & (hhN[2:1] == 2'b10);
|
||||
wire [7:0] shifterN = sample ? {hh[1], shifter[7:1]} : shifter;
|
||||
|
||||
wire ser_clk;
|
||||
baudgen #(.CLKFREQ(CLKFREQ)) _baudgen(
|
||||
.clk(clk),
|
||||
.baud({baud[30:0], 1'b0}),
|
||||
.resetq(resetq),
|
||||
.restart(startbit),
|
||||
.ser_clk(ser_clk));
|
||||
|
||||
assign valid = (bitcount == 18);
|
||||
reg [4:0] bitcountN;
|
||||
always @*
|
||||
if (startbit)
|
||||
bitcountN = 0;
|
||||
else if (!idle & !valid & ser_clk)
|
||||
bitcountN = bitcount + 5'd1;
|
||||
else if (valid & rd)
|
||||
bitcountN = 5'b11111;
|
||||
else
|
||||
bitcountN = bitcount;
|
||||
|
||||
// 3,5,7,9,11,13,15,17
|
||||
assign sample = (bitcount > 2) & bitcount[0] & !valid & ser_clk;
|
||||
assign data = shifter;
|
||||
|
||||
always @(negedge resetq or posedge clk)
|
||||
begin
|
||||
if (!resetq) begin
|
||||
hh <= 3'b111;
|
||||
bitcount <= 5'b11111;
|
||||
shifter <= 0;
|
||||
end else begin
|
||||
hh <= hhN;
|
||||
bitcount <= bitcountN;
|
||||
shifter <= shifterN;
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
module fifo_rxuart(
|
||||
input wire clk,
|
||||
input wire resetq,
|
||||
input wire [31:0] baud,
|
||||
input wire uart_rx, // UART recv wire
|
||||
input wire rd, // read strobe
|
||||
output wire valid, // has data
|
||||
output wire [7:0] data); // data
|
||||
parameter CLKFREQ = `def_clkfreq;
|
||||
|
||||
localparam ADDR_WIDTH = 6;
|
||||
localparam DATA_WIDTH = 8;
|
||||
|
||||
reg fifo_wr_en;
|
||||
(*KEEP*) wire fifo_rd_en, fifo_full, fifo_empty;
|
||||
wire [DATA_WIDTH-1:0] fifo_wr_data, fifo_rd_data;
|
||||
(*KEEP*) wire rx_avail;
|
||||
|
||||
assign valid = !fifo_empty;
|
||||
assign data = fifo_rd_data;
|
||||
assign fifo_rd_en = rd;
|
||||
|
||||
fifo #(.ADDR_WIDTH(ADDR_WIDTH)) rx_fifo(clk, ~resetq,
|
||||
fifo_wr_en, fifo_rd_en,
|
||||
fifo_wr_data, fifo_rd_data,
|
||||
fifo_full,
|
||||
fifo_empty
|
||||
);
|
||||
|
||||
rxuart #(.CLKFREQ(CLKFREQ)) _rx (
|
||||
.clk(clk),
|
||||
.resetq(resetq),
|
||||
.baud(baud),
|
||||
.uart_rx(uart_rx),
|
||||
.rd(fifo_wr_en), // strobe read signal on fifo write
|
||||
.valid(rx_avail),
|
||||
.data(fifo_wr_data));
|
||||
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if (!resetq)
|
||||
fifo_wr_en <= 0;
|
||||
else if(!fifo_wr_en && rx_avail) // ILA shows fifo_wr_en stays 0 ??
|
||||
fifo_wr_en <= 1; // pulse fifo_wr_en for one clock
|
||||
else // rx_avail goes zero one clock later
|
||||
fifo_wr_en <= 0;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module buart(
|
||||
input wire clk,
|
||||
input wire resetq,
|
||||
input wire [31:0] baud,
|
||||
input wire rx, // recv wire
|
||||
output wire tx, // xmit wire
|
||||
input wire rd, // read strobe
|
||||
input wire wr, // write strobe
|
||||
output wire valid, // has recv data
|
||||
output wire busy, // is transmitting
|
||||
input wire [7:0] tx_data,
|
||||
output wire [7:0] rx_data // data
|
||||
);
|
||||
parameter CLKFREQ = `def_clkfreq;
|
||||
|
||||
fifo_rxuart #(.CLKFREQ(CLKFREQ)) _rx (
|
||||
.clk(clk),
|
||||
.resetq(resetq),
|
||||
.baud(baud),
|
||||
.uart_rx(rx),
|
||||
.rd(rd),
|
||||
.valid(valid),
|
||||
.data(rx_data));
|
||||
uart #(.CLKFREQ(CLKFREQ)) _tx (
|
||||
.clk(clk),
|
||||
.resetq(resetq),
|
||||
.baud(baud),
|
||||
.uart_busy(busy),
|
||||
.uart_tx(tx),
|
||||
.uart_wr_i(wr),
|
||||
.uart_dat_i(tx_data));
|
||||
endmodule
|
||||
100
rtl/src/uart_tb.v
Normal file
100
rtl/src/uart_tb.v
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
//////////////////////////////////////////////////////////////////////
|
||||
// File Downloaded from http://www.nandland.com
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
// This testbench will exercise both the UART Tx and Rx.
|
||||
// It sends out byte 0xAB over the transmitter
|
||||
// It then exercises the receive by receiving byte 0x3F
|
||||
`timescale 1ns/10ps
|
||||
|
||||
module uart_tb ();
|
||||
|
||||
// Testbench uses a 10 MHz clock
|
||||
// Want to interface to 115200 baud UART
|
||||
// 10000000 / 115200 = 87 Clocks Per Bit.
|
||||
parameter c_CLOCK_PERIOD_NS = 100;
|
||||
parameter c_CLKS_PER_BIT = 87;
|
||||
parameter c_BIT_PERIOD = 8600;
|
||||
|
||||
reg r_Clock = 0;
|
||||
reg r_Tx_DV = 0;
|
||||
wire w_Tx_Done;
|
||||
reg [7:0] r_Tx_Byte = 0;
|
||||
reg r_Rx_Serial = 1;
|
||||
wire [7:0] w_Rx_Byte;
|
||||
|
||||
|
||||
// Takes in input byte and serializes it
|
||||
task UART_WRITE_BYTE;
|
||||
input [7:0] i_Data;
|
||||
integer ii;
|
||||
begin
|
||||
|
||||
// Send Start Bit
|
||||
r_Rx_Serial <= 1'b0;
|
||||
#(c_BIT_PERIOD);
|
||||
#1000;
|
||||
|
||||
|
||||
// Send Data Byte
|
||||
for (ii=0; ii<8; ii=ii+1)
|
||||
begin
|
||||
r_Rx_Serial <= i_Data[ii];
|
||||
#(c_BIT_PERIOD);
|
||||
end
|
||||
|
||||
// Send Stop Bit
|
||||
r_Rx_Serial <= 1'b1;
|
||||
#(c_BIT_PERIOD);
|
||||
end
|
||||
endtask // UART_WRITE_BYTE
|
||||
|
||||
|
||||
uart_rx #(.CLKS_PER_BIT(c_CLKS_PER_BIT)) UART_RX_INST
|
||||
(.i_Clock(r_Clock),
|
||||
.i_Rx_Serial(r_Rx_Serial),
|
||||
.o_Rx_DV(),
|
||||
.o_Rx_Byte(w_Rx_Byte)
|
||||
);
|
||||
|
||||
uart_tx #(.CLKS_PER_BIT(c_CLKS_PER_BIT)) UART_TX_INST
|
||||
(.i_Clock(r_Clock),
|
||||
.i_Tx_DV(r_Tx_DV),
|
||||
.i_Tx_Byte(r_Tx_Byte),
|
||||
.o_Tx_Active(),
|
||||
.o_Tx_Serial(),
|
||||
.o_Tx_Done(w_Tx_Done)
|
||||
);
|
||||
|
||||
|
||||
always
|
||||
#(c_CLOCK_PERIOD_NS/2) r_Clock <= !r_Clock;
|
||||
|
||||
|
||||
// Main Testing:
|
||||
initial
|
||||
begin
|
||||
|
||||
// Tell UART to send a command (exercise Tx)
|
||||
@(posedge r_Clock);
|
||||
@(posedge r_Clock);
|
||||
r_Tx_DV <= 1'b1;
|
||||
r_Tx_Byte <= 8'hAB;
|
||||
@(posedge r_Clock);
|
||||
r_Tx_DV <= 1'b0;
|
||||
@(posedge w_Tx_Done);
|
||||
|
||||
// Send a command to the UART (exercise Rx)
|
||||
@(posedge r_Clock);
|
||||
UART_WRITE_BYTE(8'h3F);
|
||||
@(posedge r_Clock);
|
||||
|
||||
// Check that the correct command was received
|
||||
if (w_Rx_Byte == 8'h3F)
|
||||
$display("Test Passed - Correct Byte Received");
|
||||
else
|
||||
$display("Test Failed - Incorrect Byte Received");
|
||||
|
||||
end
|
||||
|
||||
endmodule
|
||||
292
rtl/src/vgafb.v
Normal file
292
rtl/src/vgafb.v
Normal file
|
|
@ -0,0 +1,292 @@
|
|||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
// Project F: Display Timings
|
||||
// (C)2019 Will Green, Open Source Hardware released under the MIT License
|
||||
// Learn more at https://projectf.io
|
||||
|
||||
//128K video memory is not enough for 640x480x4
|
||||
`define RES_640_400
|
||||
//`define RES_1024_768
|
||||
|
||||
module display_timings #(
|
||||
H_RES=640, // horizontal resolution (pixels)
|
||||
V_RES=480, // vertical resolution (lines)
|
||||
H_FP=16, // horizontal front porch
|
||||
H_SYNC=96, // horizontal sync
|
||||
H_BP=48, // horizontal back porch
|
||||
V_FP=10, // vertical front porch
|
||||
V_SYNC=2, // vertical sync
|
||||
V_BP=33, // vertical back porch
|
||||
H_POL=0, // horizontal sync polarity (0:neg, 1:pos)
|
||||
V_POL=0 // vertical sync polarity (0:neg, 1:pos)
|
||||
)
|
||||
(
|
||||
input wire i_pix_clk, // pixel clock
|
||||
input wire i_rst, // reset: restarts frame (active high)
|
||||
output wire o_hs, // horizontal sync
|
||||
output wire o_vs, // vertical sync
|
||||
output wire o_de, // display enable: high during active video
|
||||
output wire o_frame, // high for one tick at the start of each frame
|
||||
output wire o_scanline, // high for one tick at the start of each scanline
|
||||
output reg o_vblank, // high during vertical blank phase
|
||||
output reg signed [15:0] o_sx, // horizontal beam position (including blanking)
|
||||
output reg signed [15:0] o_sy // vertical beam position (including blanking)
|
||||
);
|
||||
|
||||
// Horizontal: sync, active, and pixels
|
||||
localparam signed H_STA = 0 - H_FP - H_SYNC - H_BP; // horizontal start
|
||||
localparam signed HS_STA = H_STA + H_FP; // sync start
|
||||
localparam signed HS_END = HS_STA + H_SYNC; // sync end
|
||||
localparam signed HA_STA = 0; // active start
|
||||
localparam signed HA_END = H_RES - 1; // active end
|
||||
|
||||
// Vertical: sync, active, and pixels
|
||||
localparam signed V_STA = 0 - V_FP - V_SYNC - V_BP; // vertical start
|
||||
localparam signed VS_STA = V_STA + V_FP; // sync start
|
||||
localparam signed VS_END = VS_STA + V_SYNC; // sync end
|
||||
localparam signed VA_STA = 0; // active start
|
||||
localparam signed VA_END = V_RES - 1; // active end
|
||||
|
||||
// generate sync signals with correct polarity
|
||||
assign o_hs = H_POL ? (o_sx > HS_STA && o_sx <= HS_END)
|
||||
: ~(o_sx > HS_STA && o_sx <= HS_END);
|
||||
assign o_vs = V_POL ? (o_sy > VS_STA && o_sy <= VS_END)
|
||||
: ~(o_sy > VS_STA && o_sy <= VS_END);
|
||||
|
||||
// display enable: high during active period
|
||||
assign o_de = (o_sx >= 0 && o_sy >= 0);
|
||||
|
||||
// o_frame: high for one tick at the start of each frame
|
||||
assign o_frame = (o_sy == V_STA && o_sx == H_STA);
|
||||
// o_scanline: high for one tick at the start of each visible scanline
|
||||
assign o_scanline = (o_sy >= VA_STA) && (o_sy <= VA_END) && (o_sx == H_STA);
|
||||
|
||||
always @(posedge i_pix_clk)
|
||||
begin
|
||||
if(o_frame) o_vblank <= 1;
|
||||
else if (o_de) o_vblank <= 0;
|
||||
end
|
||||
|
||||
always @ (posedge i_pix_clk)
|
||||
begin
|
||||
if (i_rst) // reset to start of frame
|
||||
begin
|
||||
o_sx <= H_STA;
|
||||
o_sy <= V_STA;
|
||||
end
|
||||
else
|
||||
begin
|
||||
if (o_sx == HA_END) // end of line
|
||||
begin
|
||||
o_sx <= H_STA;
|
||||
if (o_sy == VA_END) // end of frame
|
||||
o_sy <= V_STA;
|
||||
else
|
||||
o_sy <= o_sy + 16'sh1;
|
||||
end
|
||||
else
|
||||
o_sx <= o_sx + 16'sh1;
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
// Project F: Display Controller VGA Demo
|
||||
// (C)2020 Will Green, Open source hardware released under the MIT License
|
||||
// Learn more at https://projectf.io
|
||||
|
||||
// This demo requires the following Verilog modules:
|
||||
// * display_clocks
|
||||
// * display_timings
|
||||
// * test_card_simple or another test card
|
||||
|
||||
module vgafb #(VMEM_ADDR_WIDTH = 15, VMEM_DATA_WIDTH = 32) (
|
||||
input wire cpu_clk, // cpu clock
|
||||
input wire CLK, // pixel clock
|
||||
input wire RST_BTN, // reset button
|
||||
input wire[3:0] reg_sel, // register select/address
|
||||
output wire [VMEM_DATA_WIDTH-1:0] rd_data,
|
||||
input wire [VMEM_DATA_WIDTH-1:0] wr_data,
|
||||
input wire rd_en,
|
||||
input wire wr_en,
|
||||
|
||||
output wire VGA_HS, // horizontal sync output
|
||||
output wire VGA_VS, // vertical sync output
|
||||
output wire [3:0] VGA_R, // 4-bit VGA red output
|
||||
output wire [3:0] VGA_G, // 4-bit VGA green output
|
||||
output wire [3:0] VGA_B // 4-bit VGA blue output
|
||||
);
|
||||
|
||||
localparam BITS_PER_PIXEL = 4; localparam MAX_SHIFT_COUNT = 7;
|
||||
localparam REG_RD_ADDR = 0; localparam REG_WR_ADDR = 1; localparam REG_VMEM = 2;
|
||||
localparam REG_PAL_SLOT = 3; localparam REG_PAL_DATA = 4;
|
||||
localparam REG_CTL = 5;
|
||||
|
||||
localparam COLOR_WIDTH = 12;
|
||||
localparam PALETTE_WIDTH = 4;
|
||||
|
||||
// Display Clocks
|
||||
wire pix_clk = CLK; // pixel clock
|
||||
wire clk_lock = 1; // clock locked?
|
||||
|
||||
wire vmem_rd_en;
|
||||
wire vmem_wr_en;
|
||||
wire [VMEM_DATA_WIDTH-1:0] vmem_rd_data;
|
||||
reg [VMEM_ADDR_WIDTH-1:0] cpu_rd_addr;
|
||||
reg [VMEM_ADDR_WIDTH-1:0] cpu_wr_addr;
|
||||
reg [VMEM_ADDR_WIDTH-1:0] pix_addr;
|
||||
wire [VMEM_DATA_WIDTH-1:0] pix_data;
|
||||
wire pix_rd;
|
||||
wire [VMEM_DATA_WIDTH-1:0] status;
|
||||
|
||||
assign vmem_rd_en = rd_en;
|
||||
assign vmem_wr_en = (reg_sel == REG_VMEM) && wr_en;
|
||||
assign rd_data = (reg_sel == REG_VMEM) ? vmem_rd_data :
|
||||
(reg_sel == REG_RD_ADDR) ? cpu_rd_addr :
|
||||
(reg_sel == REG_WR_ADDR) ? cpu_wr_addr :
|
||||
(reg_sel == REG_CTL) ? status :
|
||||
32'hFFFFFFFF;
|
||||
|
||||
wire [VMEM_ADDR_WIDTH-1:0] cpu_addr = vmem_wr_en ? cpu_wr_addr : cpu_rd_addr;
|
||||
|
||||
bram_tdp #(.DATA(VMEM_DATA_WIDTH),.ADDR(VMEM_ADDR_WIDTH)) vram0 (
|
||||
.a_rd(vmem_rd_en), .a_clk(cpu_clk),
|
||||
.a_wr(vmem_wr_en), .a_addr(cpu_addr), .a_din(wr_data),
|
||||
.a_dout(vmem_rd_data),
|
||||
.b_clk(pix_clk), .b_addr(pix_addr), .b_dout(pix_data),
|
||||
.b_rd(pix_rd)
|
||||
);
|
||||
|
||||
wire palette_wr_en = (reg_sel == REG_PAL_DATA) && wr_en;
|
||||
reg [PALETTE_WIDTH-1:0] palette_wr_slot;
|
||||
wire [COLOR_WIDTH-1:0] color_data;
|
||||
|
||||
palette palette0(.wr_clk(cpu_clk), .rd_clk(pix_clk), .wr_en(palette_wr_en),
|
||||
.wr_slot(palette_wr_slot), .wr_data(wr_data[COLOR_WIDTH-1:0]),
|
||||
.rd_slot(pixel[3:0]), .rd_data(color_data));
|
||||
|
||||
// Display Timings
|
||||
wire signed [15:0] sx; // horizontal screen position (signed)
|
||||
wire signed [15:0] sy; // vertical screen position (signed)
|
||||
wire h_sync; // horizontal sync
|
||||
wire v_sync; // vertical sync
|
||||
wire de; // display enable
|
||||
wire frame; // frame start
|
||||
wire scanline; // scanline start
|
||||
wire vblank; // vertical blank
|
||||
reg vblank_buf; // vertical blank in cpu clock domain
|
||||
|
||||
display_timings #( // 640x480 800x600 1280x720 1920x1080
|
||||
`ifdef RES_1024_768
|
||||
.H_RES(1024), // 640 800 1280 1920
|
||||
.V_RES(768), // 480 600 720 1080
|
||||
.H_FP(24), // 16 40 110 88
|
||||
.H_SYNC(136), // 96 128 40 44
|
||||
.H_BP(160), // 48 88 220 148
|
||||
.V_FP(3), // 10 1 5 4
|
||||
.V_SYNC(6), // 2 4 5 5
|
||||
.V_BP(29), // 33 23 20 36
|
||||
.H_POL(0), // 0 1 1 1
|
||||
.V_POL(0) // 0 1 1 1
|
||||
`endif
|
||||
`ifdef RES_640_400
|
||||
.H_RES(640),
|
||||
.V_RES(400),
|
||||
.H_FP(16),
|
||||
.H_SYNC(96),
|
||||
.H_BP(48),
|
||||
.V_FP(12),
|
||||
.V_SYNC(2),
|
||||
.V_BP(35),
|
||||
.H_POL(0),
|
||||
.V_POL(1)
|
||||
`endif
|
||||
)
|
||||
display_timings_inst (
|
||||
.i_pix_clk(CLK),
|
||||
.i_rst(!RST_BTN),
|
||||
.o_hs(h_sync),
|
||||
.o_vs(v_sync),
|
||||
.o_de(de),
|
||||
.o_frame(frame),
|
||||
.o_scanline(scanline),
|
||||
.o_vblank(vblank),
|
||||
.o_sx(sx),
|
||||
.o_sy(sy)
|
||||
);
|
||||
|
||||
wire [7:0] red;
|
||||
wire [7:0] green;
|
||||
wire [7:0] blue;
|
||||
|
||||
reg [VMEM_DATA_WIDTH-1:0] shifter;
|
||||
reg [4:0] shift_count;
|
||||
|
||||
// delayed frame signal for pix_rd
|
||||
reg frame_d;
|
||||
|
||||
assign pix_rd = frame_d || scanline || (shift_count == 2);
|
||||
|
||||
assign status = { 4'b0001, {(VMEM_DATA_WIDTH-5){1'b0}}, vblank_buf};
|
||||
|
||||
wire [BITS_PER_PIXEL-1:0] pixel = shifter[VMEM_DATA_WIDTH-1:VMEM_DATA_WIDTH-BITS_PER_PIXEL];
|
||||
|
||||
always @(posedge pix_clk) frame_d <= frame;
|
||||
|
||||
always @(posedge cpu_clk) vblank_buf <= vblank;
|
||||
|
||||
always @(posedge cpu_clk)
|
||||
begin
|
||||
if(wr_en)
|
||||
begin
|
||||
case(reg_sel)
|
||||
REG_RD_ADDR: cpu_rd_addr <= wr_data;
|
||||
REG_WR_ADDR: cpu_wr_addr <= wr_data;
|
||||
REG_VMEM: cpu_wr_addr <= cpu_wr_addr + 1; // auto-increment write addr on write
|
||||
REG_PAL_SLOT: palette_wr_slot <= wr_data[3:0];
|
||||
endcase
|
||||
end
|
||||
else
|
||||
if(rd_en && reg_sel == REG_VMEM) cpu_rd_addr <= cpu_rd_addr + 1; // auto-increment read addr on read
|
||||
end
|
||||
|
||||
always @(posedge pix_clk)
|
||||
begin
|
||||
if(scanline || shift_count == MAX_SHIFT_COUNT) // before start of a line
|
||||
begin // or at the end of a word, reset shifter with pixel data
|
||||
shift_count <= 0;
|
||||
shifter <= pix_data;
|
||||
end
|
||||
else if(de)
|
||||
begin
|
||||
shift_count <= shift_count + 1;
|
||||
shifter <= shifter << BITS_PER_PIXEL;
|
||||
end
|
||||
|
||||
if(frame) // at start of frame, reset pixel pointer
|
||||
pix_addr <= 0;
|
||||
else if(shift_count == 1) // after the first pixel, increment address
|
||||
pix_addr <= pix_addr + 1;
|
||||
end
|
||||
|
||||
// Hard-Coded RGBI palette
|
||||
// // Pixel = { red, green, blue, intensity }
|
||||
// assign red = pixel[3] ? pixel[0] ? 255 : 127: 0;
|
||||
// assign green = pixel[2] ? pixel[0] ? 255 : 127: 0;
|
||||
// assign blue = pixel[1] ? pixel[0] ? 255 : 127: 0;
|
||||
|
||||
// // VGA Output
|
||||
// // VGA Pmod is 12-bit so we take the upper nibble of each colour
|
||||
// assign VGA_HS = h_sync;
|
||||
// assign VGA_VS = v_sync;
|
||||
// assign VGA_R = de ? red[7:4] : 4'b0;
|
||||
// assign VGA_G = de ? green[7:4] : 4'b0;
|
||||
// assign VGA_B = de ? blue[7:4] : 4'b0;
|
||||
|
||||
// 12 bit RGB palette
|
||||
assign VGA_HS = h_sync;
|
||||
assign VGA_VS = v_sync;
|
||||
assign VGA_R = de ? color_data[11:8] : 4'b0;
|
||||
assign VGA_G = de ? color_data[7:4] : 4'b0;
|
||||
assign VGA_B = de ? color_data[3:0] : 4'b0;
|
||||
endmodule
|
||||
8
tests/cchangetest.pas
Normal file
8
tests/cchangetest.pas
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
program cchangetest;
|
||||
var c:char;
|
||||
begin
|
||||
repeat
|
||||
writeln('cardchanged: ', cardchanged);
|
||||
read(c);
|
||||
until c = #27;
|
||||
end.
|
||||
12
tests/readchartest.pas
Normal file
12
tests/readchartest.pas
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
program readchartest;
|
||||
var c:char;
|
||||
kbd:file;
|
||||
begin
|
||||
open(kbd, '%KBD', ModeReadonly);
|
||||
while true do
|
||||
begin
|
||||
read(kbd, c);
|
||||
writeln(ord(c));
|
||||
end;
|
||||
close(kbd);
|
||||
end.
|
||||
25
tests/readtest.pas
Normal file
25
tests/readtest.pas
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
program readtest;
|
||||
var filename:string;
|
||||
buf:char;
|
||||
f:file;
|
||||
count:integer;
|
||||
t:DateTime;
|
||||
begin
|
||||
write('Enter filename: ');
|
||||
readln(filename);
|
||||
|
||||
t := GetTime;
|
||||
writeln('start:', TimeStr(t, true));
|
||||
open(f, filename, ModeReadOnly);
|
||||
|
||||
count := 0;
|
||||
while not eof(f) do
|
||||
begin
|
||||
read(f,buf);
|
||||
count := count + 1;
|
||||
end;
|
||||
close(f);
|
||||
t := GetTime;
|
||||
writeln('end:', TimeStr(t, true));
|
||||
writeln(count, ' bytes read.');
|
||||
end.
|
||||
111
tests/test109.pas
Normal file
111
tests/test109.pas
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
program test109;
|
||||
|
||||
const screenwidth = 640;
|
||||
screenheight = 400;
|
||||
screenmidx = 319;
|
||||
screenmidy = 199;
|
||||
|
||||
xrange = 14.0;
|
||||
yrange = 3.0;
|
||||
|
||||
xmin = -7.0;
|
||||
xmax = 7.0;
|
||||
ymin = -1.5;
|
||||
ymax = 1.5;
|
||||
|
||||
xstep = 0.005;
|
||||
|
||||
|
||||
var scalex,scaley: real;
|
||||
value:real;
|
||||
curx:real;
|
||||
|
||||
testcounter:integer;
|
||||
|
||||
function screenx(x:real):integer;
|
||||
begin
|
||||
screenx := trunc((x + xmax) * scalex);
|
||||
{ writeln(x, ' -x-> ', screenx);}
|
||||
end;
|
||||
|
||||
function screeny(y:real):integer;
|
||||
begin
|
||||
screeny := trunc((ymax - y) * scaley);
|
||||
{ writeln(y, ' -y-> ', screeny); }
|
||||
end;
|
||||
|
||||
procedure drawCoords;
|
||||
begin
|
||||
drawline(screenx(xmin), screeny(0), screenx(xmax), screeny(0), 8);
|
||||
drawline(screenx(0), screeny(ymin), screenx(0), screeny(ymax), 8);
|
||||
end;
|
||||
|
||||
procedure plot(x,y:real;nr:integer);
|
||||
begin
|
||||
if (x>=xmin) and (x<=xmax)
|
||||
and (y>=ymin) and (y<=ymax) then
|
||||
putpixel( screenx(x), screeny(y), 3 + nr);
|
||||
end;
|
||||
|
||||
procedure test(x:real; delta:real);
|
||||
begin
|
||||
writeln('-----------test-----------------');
|
||||
end;
|
||||
|
||||
function squareroot(x:real):real;
|
||||
begin
|
||||
if x = 0.0 then
|
||||
squareroot := 0.0
|
||||
else
|
||||
squareroot := sqrt(x);
|
||||
end;
|
||||
|
||||
function logn(x:real):real;
|
||||
begin
|
||||
if x <= 0.0 then
|
||||
logn := 0.0
|
||||
else
|
||||
logn := ln(x);
|
||||
end;
|
||||
|
||||
function dafunc(x:real;nr:integer):real;
|
||||
begin
|
||||
{
|
||||
testcounter := testcounter + 1;
|
||||
if testcounter = 20 then
|
||||
test(x, xstep); }
|
||||
{ writeln('dafunc ', testcounter, ' x:', x, ' + 0.1:', x + 0.1); }
|
||||
case nr of
|
||||
0: dafunc := sin(x);
|
||||
1: dafunc := cos(x);
|
||||
2: dafunc := arctan(x);
|
||||
3: dafunc := tan(x);
|
||||
4: dafunc := cotan(x);
|
||||
5: dafunc := logn(x);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure graph(nr:integer);
|
||||
begin
|
||||
curx := xmin;
|
||||
{ curx := 0.0; }
|
||||
while curx < xmax do
|
||||
begin
|
||||
value := dafunc(curx, nr);
|
||||
plot(curx, value, nr);
|
||||
curx := curx + xstep;
|
||||
end;
|
||||
end;
|
||||
|
||||
begin
|
||||
initgraphics;
|
||||
scalex := screenwidth / xrange;
|
||||
scaley := screenheight / yrange;
|
||||
drawCoords;
|
||||
graph(0);
|
||||
graph(1);
|
||||
graph(2);
|
||||
graph(3);
|
||||
graph(4);
|
||||
graph(5);
|
||||
end.
|
||||
17
tests/test133.pas
Normal file
17
tests/test133.pas
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
program test133;
|
||||
var f:file;
|
||||
buf:string;
|
||||
begin
|
||||
open(f, 'newfile.text', ModeOverwrite);
|
||||
writeln(f,'This is a test file created by a Pascal program.');
|
||||
writeln(f,'There is nothing else of interest here.');
|
||||
close(f);
|
||||
|
||||
open(f, 'newfile.text', ModeReadonly);
|
||||
while not eof(f) do
|
||||
begin
|
||||
readln(f,buf);
|
||||
writeln(buf);
|
||||
end;
|
||||
close(f);
|
||||
end.
|
||||
28
tests/test159.pas
Normal file
28
tests/test159.pas
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
program test159;
|
||||
var s:string[131072];
|
||||
i:integer;
|
||||
c:char;
|
||||
buf:string;
|
||||
begin
|
||||
writeln('creating test string...');
|
||||
c := 'A';
|
||||
for i := 1 to maxlength(s) do
|
||||
begin
|
||||
appendchar(s,c);
|
||||
c := succ(c);
|
||||
if c = 'z' then
|
||||
c := 'A';
|
||||
end;
|
||||
|
||||
writeln('string length: ', length(s));
|
||||
|
||||
writeln(s[1], s[2], s[3]);
|
||||
|
||||
writeln('moving stuff...');
|
||||
repeat
|
||||
write('>');
|
||||
readln(buf);
|
||||
strmoveup(s, 1,100000,1);
|
||||
writeln(s[1], s[2], s[3]);
|
||||
until buf = 'x';
|
||||
end.
|
||||
12
tests/timetest.pas
Normal file
12
tests/timetest.pas
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
program timetest;
|
||||
var time:DateTime;
|
||||
begin
|
||||
while true do
|
||||
begin
|
||||
writeln('ticks: ', GetTicks);
|
||||
time := GetTime;
|
||||
writeln('h:', time.hours, ' m:', time.minutes, ' s:', time.seconds);
|
||||
writeln(DateStr(time), ' ', TimeStr(time,true));
|
||||
readln;
|
||||
end;
|
||||
end.
|
||||
479
tests/tree.pas
Normal file
479
tests/tree.pas
Normal file
|
|
@ -0,0 +1,479 @@
|
|||
program tree;
|
||||
type TreedataType = (TDString, TDInteger);
|
||||
type Treedata = record
|
||||
case typ:Treedatatype of
|
||||
TDString:(stringdata:string);
|
||||
TDInteger:(intdata:integer);
|
||||
end;
|
||||
type TreeNode = record
|
||||
parent: ^TreeNode;
|
||||
left,right: ^TreeNode;
|
||||
height: integer;
|
||||
key: ^string;
|
||||
data: ^Treedata;
|
||||
end;
|
||||
|
||||
type TreeRef = ^TreeNode;
|
||||
|
||||
type TreeWalkState = record
|
||||
currentNode:TreeRef;
|
||||
end;
|
||||
|
||||
var t:TreeRef;
|
||||
k:string;
|
||||
d:TreeData;
|
||||
i:integer;
|
||||
searchres:^Treedata;
|
||||
walkState:TreeWalkState;
|
||||
walkRes:TreeRef;
|
||||
|
||||
procedure mem_dump; external;
|
||||
|
||||
function makeTreeNode(var d:TreeData;var key:string;nparent:TreeRef):TreeRef;
|
||||
var newNode:TreeRef;
|
||||
newKey:^string;
|
||||
begin
|
||||
new(newNode);
|
||||
new(newKey,length(key));
|
||||
{ new(newKey); }
|
||||
new(newNode^.data);
|
||||
newKey^ := key;
|
||||
with newNode^ do
|
||||
begin
|
||||
key := newKey;
|
||||
parent := nparent;
|
||||
left := nil;
|
||||
right := nil;
|
||||
height := 1;
|
||||
data^ := d;
|
||||
end;
|
||||
makeTreeNode := newNode;
|
||||
end;
|
||||
|
||||
function MeasureTree(root:TreeRef):integer;
|
||||
var leftHeight, rightHeight:integer;
|
||||
begin
|
||||
if root = nil then
|
||||
MeasureTree := 0
|
||||
else
|
||||
begin
|
||||
if root^.left <> nil then
|
||||
leftHeight := root^.left^.height
|
||||
else
|
||||
leftHeight := 0;
|
||||
if root^.right <> nil then
|
||||
rightHeight := root^.right^.height
|
||||
else
|
||||
rightHeight := 0;
|
||||
if rightHeight > leftHeight then
|
||||
MeasureTree := rightHeight + 1
|
||||
else
|
||||
MeasureTree := leftHeight + 1;
|
||||
end;
|
||||
end;
|
||||
|
||||
function GetTreeBalance(root:TreeRef):integer;
|
||||
begin
|
||||
if root = nil then
|
||||
GetTreeBalance := 0
|
||||
else
|
||||
GetTreeBalance := MeasureTree(root^.left) - MeasureTree(root^.right);
|
||||
end;
|
||||
|
||||
function RotateTreeRight(x:TreeRef):TreeRef;
|
||||
var z,tmp:TreeRef;
|
||||
begin
|
||||
writeln('RotateTreeRight at ', x^.key^);
|
||||
z := x^.left;
|
||||
tmp := z^.right;
|
||||
z^.right := x;
|
||||
z^.parent := x^.parent;
|
||||
x^.parent := z;
|
||||
x^.left := tmp;
|
||||
if tmp <> nil then
|
||||
tmp^.parent := x;
|
||||
x^.height := MeasureTree(x);
|
||||
z^.height := MeasureTree(z);
|
||||
RotateTreeRight := z;
|
||||
end;
|
||||
|
||||
function RotateTreeLeft(x:TreeRef):TreeRef;
|
||||
var z,tmp:TreeRef;
|
||||
begin
|
||||
writeln('RotateTreeLeft at ', x^.key^);
|
||||
z := x^.right;
|
||||
tmp := z^.left;
|
||||
z^.left := x;
|
||||
z^.parent := x^.parent;
|
||||
x^.parent := z;
|
||||
x^.right := tmp;
|
||||
if tmp <> nil then
|
||||
tmp^.parent := x;
|
||||
x^.height := MeasureTree(x);
|
||||
z^.height := MeasureTree(z);
|
||||
RotateTreeLeft := z;
|
||||
end;
|
||||
|
||||
function TreeInsert4(root:TreeRef;var key:string;var data:TreeData;
|
||||
parent:TreeRef):TreeRef;
|
||||
var balance:integer;
|
||||
begin
|
||||
if root = nil then
|
||||
root := makeTreeNode(data, key, parent)
|
||||
else
|
||||
if key < root^.key^ then
|
||||
root^.left := TreeInsert4(root^.left, key, data, root)
|
||||
else
|
||||
root^.right := TreeInsert4(root^.right, key, data, root);
|
||||
|
||||
root^.height := MeasureTree(root);
|
||||
|
||||
balance := GetTreeBalance(root);
|
||||
if balance > 1 then
|
||||
begin
|
||||
if key < root^.left^.key^ then
|
||||
root := RotateTreeRight(root)
|
||||
else
|
||||
begin
|
||||
root^.left := RotateTreeLeft(root^.left);
|
||||
root := RotateTreeRight(root);
|
||||
end;
|
||||
end
|
||||
else
|
||||
if balance < -1 then
|
||||
begin
|
||||
if key > root^.right^.key^ then
|
||||
root := RotateTreeLeft(root)
|
||||
else
|
||||
begin
|
||||
root^.right := RotateTreeRight(root^.right);
|
||||
root := RotateTreeLeft(root);
|
||||
end;
|
||||
end;
|
||||
|
||||
TreeInsert4 := root;
|
||||
end;
|
||||
|
||||
procedure TreeInsert(var root:TreeRef;var key:string;var data:TreeData);
|
||||
begin
|
||||
root := TreeInsert4(root,key,data,nil);
|
||||
end;
|
||||
|
||||
procedure DisposeTreeNode(node:TreeRef);
|
||||
begin
|
||||
dispose(node^.key);
|
||||
dispose(node^.data);
|
||||
dispose(node);
|
||||
end;
|
||||
|
||||
function TreeLeftmost(node:TreeRef):TreeRef;
|
||||
begin
|
||||
TreeLeftmost := nil;
|
||||
if node <> nil then
|
||||
begin
|
||||
repeat
|
||||
TreeLeftmost := node;
|
||||
node := node^.left;
|
||||
until node = nil;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure PrintTreeRef(node:TreeRef);
|
||||
begin
|
||||
if node = nil then
|
||||
write('nil')
|
||||
else
|
||||
write(node^.key^);
|
||||
end;
|
||||
|
||||
procedure PrintTreeNode(node:TreeRef);
|
||||
begin
|
||||
write(' -');
|
||||
PrintTreeRef(node);
|
||||
if node <> nil then
|
||||
begin
|
||||
write(' ^');
|
||||
PrintTreeRef(node^.parent);
|
||||
write(' <');
|
||||
PrintTreeRef(node^.left);
|
||||
write(' >');
|
||||
PrintTreeRef(node^.right);
|
||||
end;
|
||||
writeln;
|
||||
end;
|
||||
|
||||
function TreeDeleteFn(root:TreeRef;var key:string):TreeRef;
|
||||
var tmp,oldParent:TreeRef;
|
||||
balance:integer;
|
||||
begin
|
||||
if root <> nil then
|
||||
begin
|
||||
if key < root^.key^ then
|
||||
root^.left := TreeDeleteFn(root^.left, key)
|
||||
else
|
||||
if key > root^.key^ then
|
||||
root^.right := TreeDeleteFn(root^.right, key)
|
||||
else
|
||||
begin
|
||||
if root^.left = nil then
|
||||
begin
|
||||
tmp := root;
|
||||
oldParent := root^.parent;
|
||||
root := root^.right;
|
||||
if root <> nil then
|
||||
root^.parent := oldParent;
|
||||
DisposeTreeNode(tmp);
|
||||
end
|
||||
else
|
||||
if root^.right = nil then
|
||||
begin
|
||||
tmp := root;
|
||||
oldParent := root^.parent;
|
||||
root := root^.left;
|
||||
if root <> nil then
|
||||
root^.parent := oldParent;
|
||||
DisposeTreeNode(tmp);
|
||||
end
|
||||
else
|
||||
begin
|
||||
writeln('TreeDelete search leftmost from ', root^.key^);
|
||||
PrintTreeNode(root);
|
||||
tmp := TreeLeftmost(root^.right);
|
||||
if maxlength(tmp^.key^) <> maxlength(root^.key^) then
|
||||
begin (* reallocate key, the swapped key might have a different length *)
|
||||
write('reallocating key ', length(root^.key^));
|
||||
dispose(root^.key);
|
||||
new(root^.key, length(tmp^.key^));
|
||||
writeln(' -> ', maxlength(root^.key^));
|
||||
end;
|
||||
root^.key^ := tmp^.key^;
|
||||
root^.data^ := tmp^.data^;
|
||||
writeln('TreeDelete delete leftmost ', tmp^.key^);
|
||||
PrintTreeNode(tmp);
|
||||
writeln('oldParent: ');
|
||||
PrintTreeNode(tmp^.parent);
|
||||
oldParent := tmp^.parent;
|
||||
if oldParent^.left = tmp then
|
||||
oldParent^.left := TreeDeleteFn(oldParent^.left, tmp^.key^)
|
||||
else
|
||||
if oldParent^.right = tmp then
|
||||
oldParent^.right := TreeDeleteFn(oldParent^.right, tmp^.key^)
|
||||
else
|
||||
writeln('TreeDelete internal error');
|
||||
end;
|
||||
|
||||
if root <> nil then
|
||||
begin
|
||||
root^.height := MeasureTree(root);
|
||||
balance := GetTreeBalance(root);
|
||||
if balance > 1 then
|
||||
begin
|
||||
if GetTreeBalance(root^.left) >=0 then
|
||||
root := RotateTreeRight(root)
|
||||
else
|
||||
begin
|
||||
root^.left := RotateTreeLeft(root^.left);
|
||||
root := RotateTreeRight(root);
|
||||
end;
|
||||
end
|
||||
else
|
||||
if balance < -1 then
|
||||
begin
|
||||
if GetTreeBalance(root^.right) <= 0 then
|
||||
root^.right := RotateTreeLeft(root)
|
||||
else
|
||||
begin
|
||||
root^.right := RotateTreeRight(root^.right);
|
||||
root := RotateTreeLeft(root);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
TreeDeleteFn := root;
|
||||
end;
|
||||
|
||||
procedure TreeDelete(var root:TreeRef;var key:string);
|
||||
begin
|
||||
root := TreeDeleteFn(root,key);
|
||||
end;
|
||||
|
||||
function TreeSearch(root:TreeRef;var key:string):^TreeData;
|
||||
begin
|
||||
if root <> nil then
|
||||
begin
|
||||
if key = root^.key^ then
|
||||
TreeSearch := root^.data
|
||||
else
|
||||
if key < root^.key^ then
|
||||
TreeSearch := TreeSearch(root^.left, key)
|
||||
else
|
||||
TreeSearch := TreeSearch(root^.right, key);
|
||||
end
|
||||
else
|
||||
TreeSearch := nil;
|
||||
end;
|
||||
|
||||
procedure TreeWalkStart(t:TreeRef; var state:TreeWalkState);
|
||||
begin
|
||||
(* start at leftmost node of the tree *)
|
||||
state.currentNode := TreeLeftmost(t);
|
||||
end;
|
||||
|
||||
procedure TreeWalkNext(var state:TreeWalkState;var res:TreeRef);
|
||||
var last,current,old,right:TreeRef;
|
||||
begin
|
||||
current := state.currentNode;
|
||||
|
||||
res := current;
|
||||
|
||||
if current <> nil then
|
||||
begin
|
||||
(* descending right *)
|
||||
if current^.right <> nil then
|
||||
begin
|
||||
state.currentNode := TreeLeftmost(current^.right);
|
||||
end
|
||||
else (* ascending *)
|
||||
begin
|
||||
old := current;
|
||||
repeat
|
||||
last := current;
|
||||
current := current^.parent;
|
||||
if current <> nil then
|
||||
right := current^.right;
|
||||
until (right <> last) or (current = nil); (* ascend left edges *)
|
||||
state.currentNode := current;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure indent(i:integer);
|
||||
var c:integer;
|
||||
begin
|
||||
for c := 1 to i do
|
||||
write(' ');
|
||||
end;
|
||||
|
||||
procedure PrintStringTree(node:TreeRef;level:integer);
|
||||
begin
|
||||
if node <> nil then
|
||||
begin
|
||||
if node^.left <> nil then
|
||||
PrintStringTree(node^.left, level + 1);
|
||||
indent(level);
|
||||
PrintTreeNode(node);
|
||||
if node^.right <> nil then
|
||||
PrintStringTree(node^.right, level + 1);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure DoASearch(t:TreeRef; s:string);
|
||||
var res:^TreeData;
|
||||
begin
|
||||
res := TreeSearch(t, s);
|
||||
write('searching for ',s);
|
||||
if res = nil then
|
||||
writeln(' nil')
|
||||
else
|
||||
writeln(res^.stringdata);
|
||||
end;
|
||||
|
||||
begin
|
||||
mem_dump;
|
||||
{
|
||||
t := nil;
|
||||
k := 'test1';
|
||||
d.typ := TDString;
|
||||
d.stringdata := 'data1';
|
||||
TreeInsert(t,k,d);
|
||||
|
||||
k := 'test0';
|
||||
d.typ := TDString;
|
||||
d.stringdata := 'data0';
|
||||
TreeInsert(t,k,d);
|
||||
|
||||
k := 'test3';
|
||||
d.typ := TDString;
|
||||
d.stringdata := 'data3';
|
||||
TreeInsert(t,k,d);
|
||||
|
||||
k := 'test2';
|
||||
d.typ := TDString;
|
||||
d.stringdata := 'data2';
|
||||
TreeInsert(t,k,d);
|
||||
|
||||
k := 'test4';
|
||||
d.typ := TDString;
|
||||
d.stringdata := 'data4';
|
||||
TreeInsert(t,k,d);
|
||||
|
||||
writeln('root: ', t^.key^);
|
||||
|
||||
PrintStringTree(t,1);
|
||||
}
|
||||
writeln('------------');
|
||||
|
||||
t := nil;
|
||||
d.typ := TDString;
|
||||
d.stringdata := 'data';
|
||||
|
||||
for i := 1 to 30 do
|
||||
begin
|
||||
str(i,k);
|
||||
d.stringdata := 'data' + k;
|
||||
k := 'test' + k;
|
||||
TreeInsert(t,k,d);
|
||||
|
||||
if i >99 then
|
||||
begin
|
||||
writeln('root: ', t^.key^);
|
||||
PrintStringTree(t,1);
|
||||
writeln('------------');
|
||||
readln;
|
||||
end;
|
||||
end;
|
||||
|
||||
writeln('root: ', t^.key^);
|
||||
PrintStringTree(t,1);
|
||||
writeln('------------');
|
||||
|
||||
|
||||
DoASearch(t,'test21');
|
||||
DoASearch(t,'test2');
|
||||
DoASearch(t,'test17');
|
||||
|
||||
k := 'test17';
|
||||
TreeDelete(t,k);
|
||||
writeln('root: ', t^.key^);
|
||||
PrintStringTree(t,1);
|
||||
writeln('------------');
|
||||
DoASearch(t,'test17');
|
||||
|
||||
|
||||
|
||||
TreeWalkStart(t, walkState);
|
||||
repeat
|
||||
TreeWalkNext(walkState, walkRes);
|
||||
if walkRes <> nil then
|
||||
writeln(walkRes^.data^.stringdata);
|
||||
until walkRes = nil;
|
||||
|
||||
|
||||
|
||||
for i := 1 to 30 do
|
||||
begin
|
||||
str(i,k);
|
||||
k := 'test' + k;
|
||||
writeln('deleting ', k);
|
||||
TreeDelete(t,k);
|
||||
end;
|
||||
if t <> nil then
|
||||
writeln('root: ', t^.key^)
|
||||
else
|
||||
writeln('root: nil');
|
||||
PrintStringTree(t,1);
|
||||
writeln('------------');
|
||||
|
||||
mem_dump;
|
||||
end.
|
||||
288
tests/treeimpl.pas
Normal file
288
tests/treeimpl.pas
Normal file
|
|
@ -0,0 +1,288 @@
|
|||
function makeTreeNode(var d:TreeData;var key:string;nparent:TreeRef):TreeRef;
|
||||
var newNode:TreeRef;
|
||||
newKey:^string;
|
||||
begin
|
||||
new(newNode);
|
||||
{ new(newKey,length(key)); }
|
||||
newString(newKey, length(key));
|
||||
new(newNode^.data);
|
||||
newKey^ := key;
|
||||
with newNode^ do
|
||||
begin
|
||||
key := newKey;
|
||||
parent := nparent;
|
||||
left := nil;
|
||||
right := nil;
|
||||
height := 1;
|
||||
data^ := d;
|
||||
end;
|
||||
makeTreeNode := newNode;
|
||||
end;
|
||||
|
||||
function MeasureTree(root:TreeRef):integer;
|
||||
var leftHeight, rightHeight:integer;
|
||||
begin
|
||||
if root = nil then
|
||||
MeasureTree := 0
|
||||
else
|
||||
begin
|
||||
if root^.left <> nil then
|
||||
leftHeight := root^.left^.height
|
||||
else
|
||||
leftHeight := 0;
|
||||
if root^.right <> nil then
|
||||
rightHeight := root^.right^.height
|
||||
else
|
||||
rightHeight := 0;
|
||||
if rightHeight > leftHeight then
|
||||
MeasureTree := rightHeight + 1
|
||||
else
|
||||
MeasureTree := leftHeight + 1;
|
||||
end;
|
||||
end;
|
||||
|
||||
function GetTreeBalance(root:TreeRef):integer;
|
||||
begin
|
||||
if root = nil then
|
||||
GetTreeBalance := 0
|
||||
else
|
||||
GetTreeBalance := MeasureTree(root^.left) - MeasureTree(root^.right);
|
||||
end;
|
||||
|
||||
function RotateTreeRight(x:TreeRef):TreeRef;
|
||||
var z,tmp:TreeRef;
|
||||
begin
|
||||
(* writeln('RotateTreeRight at ', x^.key^); *)
|
||||
z := x^.left;
|
||||
tmp := z^.right;
|
||||
z^.right := x;
|
||||
z^.parent := x^.parent;
|
||||
x^.parent := z;
|
||||
x^.left := tmp;
|
||||
if tmp <> nil then
|
||||
tmp^.parent := x;
|
||||
x^.height := MeasureTree(x);
|
||||
z^.height := MeasureTree(z);
|
||||
RotateTreeRight := z;
|
||||
end;
|
||||
|
||||
function RotateTreeLeft(x:TreeRef):TreeRef;
|
||||
var z,tmp:TreeRef;
|
||||
begin
|
||||
(* writeln('RotateTreeLeft at ', x^.key^); *)
|
||||
z := x^.right;
|
||||
tmp := z^.left;
|
||||
z^.left := x;
|
||||
z^.parent := x^.parent;
|
||||
x^.parent := z;
|
||||
x^.right := tmp;
|
||||
if tmp <> nil then
|
||||
tmp^.parent := x;
|
||||
x^.height := MeasureTree(x);
|
||||
z^.height := MeasureTree(z);
|
||||
RotateTreeLeft := z;
|
||||
end;
|
||||
|
||||
function TreeInsert4(root:TreeRef;var key:string;var data:TreeData;
|
||||
parent:TreeRef):TreeRef;
|
||||
var balance:integer;
|
||||
begin
|
||||
if root = nil then
|
||||
root := makeTreeNode(data, key, parent)
|
||||
else
|
||||
if key < root^.key^ then
|
||||
root^.left := TreeInsert4(root^.left, key, data, root)
|
||||
else
|
||||
root^.right := TreeInsert4(root^.right, key, data, root);
|
||||
|
||||
root^.height := MeasureTree(root);
|
||||
|
||||
balance := GetTreeBalance(root);
|
||||
if balance > 1 then
|
||||
begin
|
||||
if key < root^.left^.key^ then
|
||||
root := RotateTreeRight(root)
|
||||
else
|
||||
begin
|
||||
root^.left := RotateTreeLeft(root^.left);
|
||||
root := RotateTreeRight(root);
|
||||
end;
|
||||
end
|
||||
else
|
||||
if balance < -1 then
|
||||
begin
|
||||
if key > root^.right^.key^ then
|
||||
root := RotateTreeLeft(root)
|
||||
else
|
||||
begin
|
||||
root^.right := RotateTreeRight(root^.right);
|
||||
root := RotateTreeLeft(root);
|
||||
end;
|
||||
end;
|
||||
|
||||
TreeInsert4 := root;
|
||||
end;
|
||||
|
||||
procedure TreeInsert(var root:TreeRef;var key:string;var data:TreeData);
|
||||
begin
|
||||
root := TreeInsert4(root,key,data,nil);
|
||||
end;
|
||||
|
||||
procedure DisposeTreeNode(node:TreeRef);
|
||||
begin
|
||||
dispose(node^.key);
|
||||
dispose(node^.data);
|
||||
dispose(node);
|
||||
end;
|
||||
|
||||
function TreeLeftmost(node:TreeRef):TreeRef;
|
||||
begin
|
||||
TreeLeftmost := nil;
|
||||
if node <> nil then
|
||||
begin
|
||||
repeat
|
||||
TreeLeftmost := node;
|
||||
node := node^.left;
|
||||
until node = nil;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TreeDeleteFn(root:TreeRef;var key:string):TreeRef;
|
||||
var tmp,oldParent:TreeRef;
|
||||
balance:integer;
|
||||
begin
|
||||
if root <> nil then
|
||||
begin
|
||||
if key < root^.key^ then
|
||||
root^.left := TreeDeleteFn(root^.left, key)
|
||||
else
|
||||
if key > root^.key^ then
|
||||
root^.right := TreeDeleteFn(root^.right, key)
|
||||
else
|
||||
begin
|
||||
if root^.left = nil then
|
||||
begin
|
||||
tmp := root;
|
||||
oldParent := root^.parent;
|
||||
root := root^.right;
|
||||
if root <> nil then
|
||||
root^.parent := oldParent;
|
||||
DisposeTreeNode(tmp);
|
||||
end
|
||||
else
|
||||
if root^.right = nil then
|
||||
begin
|
||||
tmp := root;
|
||||
oldParent := root^.parent;
|
||||
root := root^.left;
|
||||
if root <> nil then
|
||||
root^.parent := oldParent;
|
||||
DisposeTreeNode(tmp);
|
||||
end
|
||||
else
|
||||
begin
|
||||
tmp := TreeLeftmost(root^.right);
|
||||
root^.key^ := tmp^.key^;
|
||||
root^.data^ := tmp^.data^;
|
||||
oldParent := tmp^.parent;
|
||||
if oldParent^.left = tmp then
|
||||
oldParent^.left := TreeDeleteFn(oldParent^.left, tmp^.key^)
|
||||
else
|
||||
if oldParent^.right = tmp then
|
||||
oldParent^.right := TreeDeleteFn(oldParent^.right, tmp^.key^)
|
||||
else
|
||||
begin
|
||||
writeln('TreeDelete internal error at', root^.key^);
|
||||
end;
|
||||
end;
|
||||
|
||||
if root <> nil then
|
||||
begin
|
||||
root^.height := MeasureTree(root);
|
||||
balance := GetTreeBalance(root);
|
||||
if balance > 1 then
|
||||
begin
|
||||
if GetTreeBalance(root^.left) >=0 then
|
||||
root := RotateTreeRight(root)
|
||||
else
|
||||
begin
|
||||
root^.left := RotateTreeLeft(root^.left);
|
||||
root := RotateTreeRight(root);
|
||||
end;
|
||||
end
|
||||
else
|
||||
if balance < -1 then
|
||||
begin
|
||||
if GetTreeBalance(root^.right) <= 0 then
|
||||
root := RotateTreeLeft(root)
|
||||
else
|
||||
begin
|
||||
root^.right := RotateTreeRight(root^.right);
|
||||
root := RotateTreeLeft(root);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
TreeDeleteFn := root;
|
||||
end;
|
||||
|
||||
procedure TreeDelete(var root:TreeRef;var key:string);
|
||||
begin
|
||||
root := TreeDeleteFn(root,key);
|
||||
end;
|
||||
|
||||
function TreeSearch(root:TreeRef;var key:string):TreeDataRef;
|
||||
begin
|
||||
if root <> nil then
|
||||
begin
|
||||
if key = root^.key^ then
|
||||
TreeSearch := root^.data
|
||||
else
|
||||
if key < root^.key^ then
|
||||
TreeSearch := TreeSearch(root^.left, key)
|
||||
else
|
||||
TreeSearch := TreeSearch(root^.right, key);
|
||||
end
|
||||
else
|
||||
TreeSearch := nil;
|
||||
end;
|
||||
|
||||
procedure TreeWalkStart(t:TreeRef; var state:TreeWalkState);
|
||||
begin
|
||||
(* start at leftmost node of the tree *)
|
||||
state.currentNode := TreeLeftmost(t);
|
||||
end;
|
||||
|
||||
procedure TreeWalkNext(var state:TreeWalkState;var res:TreeRef);
|
||||
var last,current,right:TreeRef;
|
||||
begin
|
||||
current := state.currentNode;
|
||||
|
||||
res := current;
|
||||
|
||||
if current <> nil then
|
||||
begin
|
||||
(* descending right *)
|
||||
if current^.right <> nil then
|
||||
begin
|
||||
state.currentNode := TreeLeftmost(current^.right);
|
||||
end
|
||||
else (* ascending *)
|
||||
begin
|
||||
repeat
|
||||
last := current;
|
||||
current := current^.parent;
|
||||
if current <> nil then
|
||||
right := current^.right;
|
||||
until (right <> last) or (current = nil); (* ascend left edges *)
|
||||
state.currentNode := current;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TreeWalkFirst(t:TreeRef; var state:TreeWalkState; var first:TreeRef);
|
||||
begin
|
||||
TreeWalkStart(t, state);
|
||||
TreeWalkNext(state, first);
|
||||
end;
|
||||
25
tests/treetypes.pas
Normal file
25
tests/treetypes.pas
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
type TreedataType = (TDString, TDInteger);
|
||||
|
||||
type Treedata = record
|
||||
case typ:Treedatatype of
|
||||
TDString:(stringdata:string);
|
||||
TDInteger:(intdata:integer);
|
||||
end;
|
||||
}
|
||||
type StringRef = ^string;
|
||||
|
||||
type TreeNode = record
|
||||
parent: ^TreeNode;
|
||||
left,right: ^TreeNode;
|
||||
height: integer;
|
||||
key: StringRef;
|
||||
data: ^Treedata;
|
||||
end;
|
||||
|
||||
type TreeRef = ^TreeNode;
|
||||
TreeDataRef = ^Treedata;
|
||||
|
||||
type TreeWalkState = record
|
||||
currentNode:TreeRef;
|
||||
end;
|
||||
15
tests/umlaut.pas
Normal file
15
tests/umlaut.pas
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
(*
|
||||
test program for
|
||||
multibyte characters
|
||||
and tabs
|
||||
*)
|
||||
program umlaut;
|
||||
var s:string = 'ÄÖÜß';
|
||||
begin
|
||||
writeln('Falsches Üben von');
|
||||
writeln('Xylophonmusik quält jeden');
|
||||
writeln('größeren Zwerg.');
|
||||
writeln;
|
||||
writeln(s);
|
||||
writeln(length(s));
|
||||
end.
|
||||
7
tridoraemu/IOHandler.go
Normal file
7
tridoraemu/IOHandler.go
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
// Copyright 2021-2024 Sebastian Lederer. See the file LICENSE.md for details
|
||||
package main
|
||||
|
||||
type IOHandler interface {
|
||||
read(byteaddr word) (word, error)
|
||||
write(value word, byteaddr word) (error)
|
||||
}
|
||||
17
tridoraemu/LICENSE.md
Normal file
17
tridoraemu/LICENSE.md
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# Copyright and Licensing
|
||||
|
||||
All files, except where explicitly stated otherwise, are licensed according to the BSD-3-Clause license as follows:
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
Copyright 2024 Sebastian Lederer
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
53
tridoraemu/README.md
Normal file
53
tridoraemu/README.md
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
# Tridora Emulator
|
||||
- an emulator for the Tridora CPU / System
|
||||
- emulates the CPU, UART, SD-Card controller, VGA controller
|
||||
- supports reading the tick counter from the interrupt controller, but does not support any interrupts
|
||||
- written in Golang
|
||||
|
||||
## Getting started
|
||||
From the command line, run the *tridoraemu* or *tridoraemu.exe* program inside the *tridoraemu* directory (see below for details).
|
||||
|
||||
A precompiled binary for Windows is provided.
|
||||
|
||||
To build the program yourself, you need to have the Go language installed on your system. Building has been tested on Windows and Linux.
|
||||
|
||||
## Building
|
||||
Run the following commands inside the *tridoraemu* directory:
|
||||
|
||||
go get
|
||||
go build
|
||||
|
||||
On the first run, this may take a while as the go build system fetches some external libraries and compiles them.
|
||||
|
||||
## Running the emulator
|
||||
Start the *tridoraemu* binary in the same directory as the SD-Card image file (*sdcard.img*) and the ROM file (*rommon.prog*). It needs to be started on the command line as it uses the terminal window for the serial console. On startup, the emulator opens the VGA framebuffer window which is only used for graphics output.
|
||||
|
||||
|
||||
The Tridora software (esp. the editor) requires a decent vt100-compatible (plus colors) terminal emulator. It has been successfully tested with (new) Windows Terminal, Alacritty, WezTerm and xterm.
|
||||
|
||||
The color scheme in the editor is meant for a dark terminal background.
|
||||
|
||||
The runtime system expects the Backspace key to send the DEL character (ASCII 127).
|
||||
|
||||
## Stopping the emulator
|
||||
To stop the emulator, close the VGA framebuffer window.
|
||||
The emulator will also stop if it encounters an infinite loop (a BRANCH @+0 instruction).
|
||||
|
||||
## Things to try out
|
||||
On the ROM monitor prompt, press *B* to boot from the SD-card image. This should boot into the shell, which will first require you to enter the current date and time.
|
||||
|
||||
In the shell, try the *L* command to list directories and the *V* command to change volumes. The *Examples* volume contains some example programs in source form.
|
||||
|
||||
The programs *lines*, *conway* and *mandelbrot*, among others, show some (hopefully) interesting VGA graphics. The *viewpict* program can show image files (*.pict files) which contain 640x400x4 bitmaps. A few sample image files are provided.
|
||||
|
||||
To compile a program, set the file name (e.g. *lines.pas*) with the *W* command in the shell. Then, use *B* and *R* to build and run the program.
|
||||
|
||||
To edit the source file, have the name set with *W* and then use the *E* shell command. Inside the editor, press F1 for the help screen (and RETURN to leave the help screen). Control-X exits the editor, abandoning any changes.
|
||||
|
||||
The volume *Testvolume 1* (note the space) contains a precompiled game called *chase*. This is a game that was written for UCSD Pascal around 1980, and compiles with a few lines of changes with the Tridora Pascal compiler. The source code is also provided on that volume.
|
||||
|
||||
You can run the program with the *O* command in the shell (just press Return for the program arguments), or you can set the workfile name with *W* and then use the *R* command.
|
||||
|
||||
The *K* command in the shell is used to reclaim the space occupied by deleted or overwritten files.
|
||||
|
||||
A running program can be terminated by pressing Control-C, but only if the program is expecting keyboard input at that time.
|
||||
35
tridoraemu/console.go
Normal file
35
tridoraemu/console.go
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
// +build !windows
|
||||
// Copyright 2021-2024 Sebastian Lederer. See the file LICENSE.md for details
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
type ConsoleState struct {
|
||||
state term.State
|
||||
}
|
||||
|
||||
func SetRawConsole() (*ConsoleState, error) {
|
||||
oldState, err := term.MakeRaw(int(os.Stdin.Fd()))
|
||||
|
||||
return &ConsoleState{*oldState}, err
|
||||
}
|
||||
|
||||
func RestoreConsole(st *ConsoleState) error {
|
||||
return term.Restore(int(os.Stdin.Fd()), &st.state)
|
||||
}
|
||||
|
||||
func ConsoleRead(buf []byte) (count int, err error) {
|
||||
n, e := os.Stdin.Read(buf)
|
||||
return n, e
|
||||
}
|
||||
|
||||
func ConsoleWrite(char byte) (err error) {
|
||||
buf := make([] byte, 1)
|
||||
buf[0] = char
|
||||
_ , e := os.Stdout.Write(buf)
|
||||
return e
|
||||
}
|
||||
75
tridoraemu/console_windows.go
Normal file
75
tridoraemu/console_windows.go
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
// +build windows
|
||||
// Copyright 2021-2024 Sebastian Lederer. See the file LICENSE.md for details
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type ConsoleState struct {
|
||||
modeStdin uint32
|
||||
modeStdout uint32
|
||||
}
|
||||
|
||||
func SetRawConsole() (*ConsoleState, error) {
|
||||
var stIn uint32
|
||||
var stOut uint32
|
||||
|
||||
stdinFd := os.Stdin.Fd()
|
||||
|
||||
if err := windows.GetConsoleMode(windows.Handle(stdinFd), &stIn); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
raw := stIn &^ (windows.ENABLE_ECHO_INPUT | windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT)
|
||||
raw |= windows.ENABLE_VIRTUAL_TERMINAL_INPUT
|
||||
if err := windows.SetConsoleMode(windows.Handle(stdinFd), raw); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
/*
|
||||
stdoutFd := os.Stdout.Fd()
|
||||
|
||||
if err := windows.GetConsoleMode(windows.Handle(stdoutFd), &stOut); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
raw = stOut | windows.ENABLE_VIRTUAL_TERMINAL_INPUT | windows.ENABLE_PROCESSED_OUTPUT
|
||||
if err := windows.SetConsoleMode(windows.Handle(stdoutFd), raw); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
*/
|
||||
return &ConsoleState{stIn,stOut}, nil
|
||||
}
|
||||
|
||||
func RestoreConsole(st *ConsoleState) error {
|
||||
stdinFd := os.Stdin.Fd()
|
||||
stdoutFd := os.Stdout.Fd()
|
||||
|
||||
err := windows.SetConsoleMode(windows.Handle(stdinFd), st.modeStdin)
|
||||
if err != nil { return err }
|
||||
err = windows.SetConsoleMode(windows.Handle(stdoutFd), st.modeStdin)
|
||||
return err
|
||||
}
|
||||
|
||||
func ConsoleRead(buf []byte) (count int, err error) {
|
||||
n, e := os.Stdin.Read(buf)
|
||||
if e == io.EOF { // ugly hack to handle ^Z on windows
|
||||
// this can probably be done in a better way
|
||||
// but tbh I am glad it works and I don't
|
||||
// have to dig deeper into that windows
|
||||
// console i/o crap
|
||||
n = 1; buf[0] = 26
|
||||
return n, nil
|
||||
}
|
||||
return n, e
|
||||
}
|
||||
|
||||
func ConsoleWrite(char byte) (err error) {
|
||||
buf := make([] byte, 1)
|
||||
buf[0] = char
|
||||
_ , err = os.Stdout.Write(buf)
|
||||
return err
|
||||
}
|
||||
|
||||
467
tridoraemu/cpu.go
Normal file
467
tridoraemu/cpu.go
Normal file
|
|
@ -0,0 +1,467 @@
|
|||
// Copyright 2021-2024 Sebastian Lederer. See the file LICENSE.md for details
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type word uint32
|
||||
const wordbits = 32
|
||||
const wordbytes = 4
|
||||
const wordmask = 0xFFFFFFFF
|
||||
const hsbmask = 0x80000000
|
||||
const estackDepth = 64
|
||||
type CPU struct {
|
||||
ESP word;
|
||||
X word;
|
||||
PC, FP,BP,RP word;
|
||||
IR,IV word;
|
||||
|
||||
estack [estackDepth] word;
|
||||
|
||||
mem *Mem;
|
||||
|
||||
stopped bool
|
||||
trace bool
|
||||
singlestep bool
|
||||
}
|
||||
|
||||
func sign_extend(bits word, wordbits int) int {
|
||||
signmask := word(1 << (wordbits - 1))
|
||||
signbit := (bits & signmask) != 0
|
||||
// fmt.Printf("sign_extend %b %v signmask %08X signbit %v\n", bits, wordbits, signmask, signbit)
|
||||
if signbit {
|
||||
return int(bits & ^signmask) - int(signmask)
|
||||
} else {
|
||||
return int(bits)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CPU) initialize() {
|
||||
c.ESP = 0
|
||||
c.PC = 0
|
||||
c.X = 0
|
||||
c.FP = 65020 // these are the values set by the ROM monitor
|
||||
c.RP = 65024 // for FP and RP
|
||||
|
||||
c.singlestep = false
|
||||
}
|
||||
|
||||
func (c *CPU) printEstack() {
|
||||
fmt.Printf("[")
|
||||
for i := word(1); i <= c.ESP; i++ {
|
||||
fmt.Printf(" %08X", c.estack[i])
|
||||
}
|
||||
fmt.Printf(" %08X ] (%v)\n", c.X, c.ESP)
|
||||
}
|
||||
|
||||
func (c *CPU) showStep(desc string, operand int, opWidth int) {
|
||||
if !c.trace { return }
|
||||
|
||||
var opStr string
|
||||
if opWidth == 0 {
|
||||
opStr = ""
|
||||
} else {
|
||||
opStr = fmt.Sprintf(" %04X", operand)
|
||||
}
|
||||
|
||||
fmt.Printf("%08x %-10s%5s ", c.PC, desc, opStr)
|
||||
c.printEstack()
|
||||
}
|
||||
|
||||
func (c *CPU) getOperand(insWord word, bits int) int {
|
||||
return int(insWord & word((1 << bits) - 1))
|
||||
}
|
||||
|
||||
func (c *CPU) getSignedOperand(insWord word, bits int) int {
|
||||
bitValue := (1 << (bits - 1))
|
||||
signBit := insWord & word(bitValue)
|
||||
bitMask := (bitValue << 1) - 1
|
||||
//fmt.Printf("getSignedOperand bitValue: %v, bitMask: %v\n", bitValue, bitMask)
|
||||
o := int(insWord & word(bitMask))
|
||||
if signBit != 0 {
|
||||
o = int(o) - (bitValue << 1)
|
||||
}
|
||||
//fmt.Printf("getSignedOperand: %v %v -> %d\n",insWord, bits, o)
|
||||
return o
|
||||
}
|
||||
|
||||
func (c *CPU) getBit(insWord word, bitnum int) int {
|
||||
return int(insWord >> bitnum) & 1
|
||||
}
|
||||
|
||||
func (c *CPU) getBits(insWord word, mask word, shiftCount int) word {
|
||||
return (insWord & mask) >> shiftCount
|
||||
}
|
||||
|
||||
func (c *CPU) getSignedBits(insWord word, mask word, shiftCount int) int {
|
||||
result := (insWord & mask) >> shiftCount
|
||||
signMask := ((mask >> shiftCount) + 1) >> 1
|
||||
//fmt.Printf("getSignedBits %016b signMask %v signBit %v\n", insWord, signMask, insWord & signMask)
|
||||
if result & signMask != 0 {
|
||||
return - int(result & ^signMask)
|
||||
} else {
|
||||
return int(result)
|
||||
}
|
||||
}
|
||||
|
||||
func (c * CPU) addToWord(v word, offset int) word {
|
||||
if offset < 0 {
|
||||
v -= word(-offset)
|
||||
} else {
|
||||
v += word(offset)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func (c *CPU) step() error {
|
||||
nPC := c.PC
|
||||
nFP := c.FP
|
||||
nBP := c.BP
|
||||
nRP := c.RP
|
||||
nX := c.X
|
||||
nESP := c.ESP
|
||||
|
||||
Y := c.estack[c.ESP]
|
||||
|
||||
insWord, err := c.mem.read(c.PC)
|
||||
if err != nil { return err }
|
||||
if c.PC % 4 == 0 {
|
||||
insWord = insWord >> 16
|
||||
} else {
|
||||
insWord = insWord & 0xFFFF
|
||||
}
|
||||
|
||||
baseIns := insWord >> 13
|
||||
|
||||
nPC += 2
|
||||
|
||||
x2y := false
|
||||
deltaESP := 0
|
||||
|
||||
oplen := 0
|
||||
|
||||
switch baseIns {
|
||||
// BRANCH
|
||||
case 0b000:
|
||||
operand := c.getSignedOperand(insWord, 13)
|
||||
|
||||
c.showStep("BRANCH", operand, 13)
|
||||
|
||||
nPC = word(int(c.PC) + operand)
|
||||
|
||||
if operand == 0 {
|
||||
fmt.Printf("BRANCH 0 encountered - stopped at PC %08X\n", nPC)
|
||||
c.stopped = true
|
||||
}
|
||||
// ALU
|
||||
case 0b001:
|
||||
aluop := c.getBits(insWord, 0b0001111000000000, 9)
|
||||
deltaESP = c.getSignedBits(insWord, 0b0000000000110000, 4)
|
||||
ext := c.getBit(insWord, 7) != 0
|
||||
x2y = c.getBit(insWord, 6) != 0
|
||||
operand := c.getOperand(insWord, 4)
|
||||
// fmt.Printf("aluop %v %v %v %v %v\n", aluop, s, ext, x2y, operand)
|
||||
name := "ALU"
|
||||
switch aluop {
|
||||
case 0:
|
||||
name = "ADD"
|
||||
nX = c.X + Y
|
||||
case 1:
|
||||
name = "SUB"
|
||||
nX = Y - c.X
|
||||
case 2:
|
||||
name = "NOT"
|
||||
nX = ^ c.X
|
||||
case 3:
|
||||
name = "AND"
|
||||
nX = c.X & Y
|
||||
case 4:
|
||||
name = "OR"
|
||||
nX = c.X | Y
|
||||
case 5:
|
||||
name = "XOR"
|
||||
nX = c.X ^ Y
|
||||
case 6:
|
||||
name = "CMP"
|
||||
oplen = 2
|
||||
cmp_i := c.getBit(insWord,2) != 0
|
||||
cmp_eq := c.getBit(insWord,1) != 0
|
||||
cmp_lt := c.getBit(insWord,0) != 0
|
||||
s_x := sign_extend(c.X, wordbits)
|
||||
s_y := sign_extend(Y, wordbits)
|
||||
result := (cmp_eq && (s_x == s_y)) || (cmp_lt && (s_y < s_x))
|
||||
if cmp_i { result = !result }
|
||||
if result { nX = 1 } else { nX = 0 }
|
||||
case 7:
|
||||
name = "Y"
|
||||
if !x2y && (deltaESP == -1) { name = "DROP" }
|
||||
if x2y && (deltaESP == 0) { name = "SWAP" }
|
||||
nX = Y
|
||||
case 8:
|
||||
name = "SHR"
|
||||
nX = c.X >> 1
|
||||
if ext {
|
||||
nX = nX | (c.X & hsbmask)
|
||||
}
|
||||
case 9:
|
||||
name = "SHL"
|
||||
nX = c.X << 1
|
||||
if operand & 2 != 0 {
|
||||
nX = nX << 1
|
||||
}
|
||||
oplen = 2
|
||||
case 10:
|
||||
name = "INC"
|
||||
oplen = 4
|
||||
if (operand == 0) && (deltaESP == 1) && x2y { name = "DUP" }
|
||||
nX = c.X + word(operand)
|
||||
case 11:
|
||||
name = "DEC"
|
||||
oplen = 4
|
||||
nX = c.X - word(operand)
|
||||
case 12:
|
||||
name = "CMPU"
|
||||
oplen = 2
|
||||
cmp_i := c.getBit(insWord,2) != 0
|
||||
cmp_eq := c.getBit(insWord,1) != 0
|
||||
cmp_lt := c.getBit(insWord,0) != 0
|
||||
result := (cmp_eq && (c.X == Y)) || (cmp_lt && (Y < c.X))
|
||||
if cmp_i { result = !result }
|
||||
if result { nX = 1 } else { nX = 0 }
|
||||
case 13:
|
||||
name = "BPLC"
|
||||
nX = (c.X & 0xFF) << ((3 - (Y & 3)) * 8)
|
||||
case 14:
|
||||
name = "BROT"
|
||||
nX = ((c.X & 0x00FFFFFF) << 8 ) | ((c.X & 0xFF000000) >> 24)
|
||||
case 15:
|
||||
name = "BSEL"
|
||||
shift := (3 - (Y & 3)) * 8
|
||||
nX = (c.X >> shift) & 0xFF
|
||||
}
|
||||
c.showStep(name, operand, oplen)
|
||||
// STORE
|
||||
case 0b010:
|
||||
operand := c.getOperand(insWord, 13)
|
||||
var ea word
|
||||
var name string
|
||||
if (insWord & 1) == 1 {
|
||||
name = "STORE.B"
|
||||
ea = c.BP + word(operand)
|
||||
} else {
|
||||
name = "STORE"
|
||||
ea = c.FP + word(operand)
|
||||
}
|
||||
|
||||
c.showStep(name, operand, oplen)
|
||||
|
||||
err = c.mem.write(c.X, ea)
|
||||
if err != nil { return err }
|
||||
|
||||
deltaESP = -1
|
||||
nX = Y
|
||||
// XFER
|
||||
case 0b011:
|
||||
var name string
|
||||
|
||||
deltaRP := c.getSignedBits(insWord, 0b0000001100000000, 8)
|
||||
deltaESP = c.getSignedBits(insWord, 0b0000000000110000, 4)
|
||||
r2p := c.getBit(insWord, 7) != 0
|
||||
p2r := c.getBit(insWord, 6) != 0
|
||||
x2p := c.getBit(insWord, 0) != 0
|
||||
|
||||
if deltaRP >= 0 {
|
||||
nRP = c.RP + word(deltaRP * wordbytes)
|
||||
} else {
|
||||
nRP = c.RP - word(-deltaRP * wordbytes)
|
||||
}
|
||||
|
||||
if (deltaRP == 1) && (deltaESP == -1) && p2r && x2p {
|
||||
name = "CALL"
|
||||
} else
|
||||
if (deltaRP == -1) && (deltaESP == 0) && r2p {
|
||||
name = "RET"
|
||||
} else
|
||||
if (deltaRP == 0) && (deltaESP == -1) && x2p && !p2r {
|
||||
name = "JUMP"
|
||||
} else {
|
||||
var b strings.Builder
|
||||
b.WriteString("XFER")
|
||||
if deltaRP == -1 { b.WriteString(".RSM1") }
|
||||
if deltaRP == 1 { b.WriteString(".RS1") }
|
||||
if deltaESP == -1 { b.WriteString(".SM1") }
|
||||
if deltaESP == 1 { b.WriteString(".S1") }
|
||||
if r2p { b.WriteString(".R2P") }
|
||||
if p2r { b.WriteString(".P2R") }
|
||||
if x2p { b.WriteString(".X2P") }
|
||||
name = b.String()
|
||||
}
|
||||
|
||||
c.showStep(name, 0, 0)
|
||||
|
||||
if r2p {
|
||||
nPC, err = c.mem.read(c.RP)
|
||||
if err != nil { return err }
|
||||
}
|
||||
if p2r {
|
||||
err = c.mem.write(nPC, nRP)
|
||||
if err != nil { return err }
|
||||
}
|
||||
if x2p {
|
||||
nPC = c.X
|
||||
nX = Y
|
||||
}
|
||||
|
||||
// LOAD
|
||||
case 0b100:
|
||||
operand := c.getOperand(insWord, 13)
|
||||
var ea word
|
||||
var name string
|
||||
if (insWord & 1) == 1 {
|
||||
name = "LOAD.B"
|
||||
operand &= ^1
|
||||
ea = c.BP + word(operand)
|
||||
} else {
|
||||
name = "LOAD"
|
||||
ea = c.FP + word(operand)
|
||||
}
|
||||
|
||||
c.showStep(name, operand, oplen)
|
||||
|
||||
deltaESP = 1
|
||||
x2y = true
|
||||
|
||||
nX, err = c.mem.read(ea)
|
||||
if err != nil { return err }
|
||||
// CBRANCH
|
||||
case 0b101:
|
||||
operand := c.getSignedOperand(insWord, 13)
|
||||
var name string
|
||||
invert := (operand & 1) == 0
|
||||
operand = operand & -2 // clear bit 0
|
||||
|
||||
if invert { name = "CBRANCH.Z" } else { name = "CBRANCH" }
|
||||
|
||||
c.showStep(name, operand, 13)
|
||||
|
||||
deltaESP = -1
|
||||
nX = Y
|
||||
|
||||
if (c.X != 0 && !invert) || (c.X == 0 && invert) {
|
||||
nPC = word(int(c.PC) + operand)
|
||||
}
|
||||
// LOADC
|
||||
case 0b110:
|
||||
operand := c.getSignedOperand(insWord, 13)
|
||||
oplen = 13
|
||||
|
||||
c.showStep("LOADC", operand, oplen)
|
||||
|
||||
deltaESP = 1
|
||||
x2y = true
|
||||
nX = word(operand)
|
||||
// EXT
|
||||
case 0b111:
|
||||
extop := c.getBits(insWord, 0b0001111000000000, 10)
|
||||
deltaESP = c.getSignedBits(insWord, 0b0000000000110000, 4)
|
||||
writeFlag := c.getBit(insWord, 9) != 0
|
||||
// signExtend := c.getBit(insWord,7) != 0
|
||||
x2y = c.getBit(insWord, 6) != 0
|
||||
operand := c.getOperand(insWord, 4)
|
||||
|
||||
var name string
|
||||
|
||||
switch extop {
|
||||
// LOADREG/STOREREG
|
||||
case 0:
|
||||
oplen = 4
|
||||
if writeFlag {
|
||||
name = "STOREREG"
|
||||
switch operand {
|
||||
case 0: nFP = c.X
|
||||
case 1: nBP = c.X
|
||||
case 2: nRP = c.X
|
||||
case 3: c.IV = c.X // should be nIV
|
||||
case 4: c.IR = c.X // should be nIR
|
||||
default: fmt.Errorf("Invalid STOREREG operand %v at %08X", operand, c.PC)
|
||||
}
|
||||
|
||||
c.showStep(name, operand, oplen)
|
||||
deltaESP = -1
|
||||
x2y = false
|
||||
nX = Y
|
||||
} else {
|
||||
name = "LOADREG"
|
||||
switch operand {
|
||||
case 0: nX = c.FP
|
||||
case 1: nX = c.BP
|
||||
case 2: nX = c.RP
|
||||
case 3: nX = c.IV
|
||||
case 4: nX = c.IR
|
||||
case 5: nX = c.ESP
|
||||
default: fmt.Errorf("Invalid LOADREG operand %v at %08X", operand, c.PC)
|
||||
}
|
||||
c.showStep(name, operand, oplen)
|
||||
deltaESP = 1
|
||||
x2y = true
|
||||
}
|
||||
// LOADI/STOREI
|
||||
case 1:
|
||||
if writeFlag { name = "STOREI" } else { name = "LOADI" }
|
||||
c.showStep(name, operand, oplen)
|
||||
|
||||
if writeFlag {
|
||||
oplen = 4
|
||||
err = c.mem.write(c.X, Y)
|
||||
if err != nil { return err }
|
||||
nX = Y + word(operand)
|
||||
} else {
|
||||
nX, err = c.mem.read(c.X)
|
||||
if err != nil { return err }
|
||||
}
|
||||
|
||||
// FPADJ
|
||||
case 3:
|
||||
operand := c.getSignedOperand(insWord, 10)
|
||||
oplen = 10
|
||||
nFP = c.FP + word(operand)
|
||||
deltaESP = 0
|
||||
x2y = false
|
||||
c.showStep("FPADJ", operand, oplen)
|
||||
// LOADREL
|
||||
case 5:
|
||||
offset := c.getOperand(insWord, 10)
|
||||
|
||||
c.showStep("LOADREL", offset, 10)
|
||||
|
||||
nX, err = c.mem.read(c.PC + word(offset))
|
||||
if err != nil { return err }
|
||||
x2y = true
|
||||
deltaESP = 1
|
||||
default:
|
||||
return fmt.Errorf("Invalid EXT instruction %v at %08X", extop, c.PC)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("Invalid instruction %04X at %08X", insWord, c.PC)
|
||||
}
|
||||
|
||||
nESP = c.addToWord(nESP, deltaESP)
|
||||
if nESP < 0 || nESP >= estackDepth {
|
||||
return fmt.Errorf("estack overflow %v at %08X", nESP, c.PC)
|
||||
}
|
||||
|
||||
if x2y {
|
||||
c.estack[nESP] = c.X
|
||||
}
|
||||
|
||||
c.PC = nPC
|
||||
c.FP = nFP
|
||||
c.BP = nBP
|
||||
c.X = nX
|
||||
c.RP = nRP
|
||||
c.ESP = nESP
|
||||
|
||||
return nil
|
||||
}
|
||||
132
tridoraemu/framebuffer.go
Normal file
132
tridoraemu/framebuffer.go
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
// Copyright 2021-2024 Sebastian Lederer. See the file LICENSE.md for details
|
||||
package main
|
||||
|
||||
import (
|
||||
// "fmt"
|
||||
"image/color"
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
)
|
||||
|
||||
const VmemWords = 32768
|
||||
const PaletteSlots = 16
|
||||
const FB_RA = 0
|
||||
const FB_WA = 1
|
||||
const FB_IO = 2
|
||||
const FB_PS = 3
|
||||
const FB_PD = 4
|
||||
const FB_CTL= 5
|
||||
|
||||
const PixelMask = 0b11110000000000000000000000000000
|
||||
const PixelPerWord = 8
|
||||
const VmemWidth = 32
|
||||
const BitsPerPixel = 4
|
||||
const ScreenWidth = 640
|
||||
const ScreenHeight = 400
|
||||
const WordsPerLine = ScreenWidth / PixelPerWord
|
||||
|
||||
type Framebuffer struct {
|
||||
framebuffer *ebiten.Image
|
||||
palette [PaletteSlots] color.Color
|
||||
readAddr word
|
||||
writeAddr word
|
||||
paletteSlot word
|
||||
vmem [VmemWords]word
|
||||
readCount int
|
||||
}
|
||||
|
||||
func (f *Framebuffer) initialize() {
|
||||
f.framebuffer = ebiten.NewImage(ScreenWidth, ScreenHeight)
|
||||
for i := 0; i <PaletteSlots; i++ {
|
||||
f.palette[i] = color.RGBA{0,0,0,0}
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Framebuffer) read(byteaddr word) (word, error) {
|
||||
result := word(0)
|
||||
|
||||
addr := byteaddr & 0x7F
|
||||
switch addr {
|
||||
case FB_RA: result = f.readAddr
|
||||
case FB_WA: result = f.writeAddr
|
||||
case FB_IO: result = f.readVmem()
|
||||
case FB_PS: result = f.paletteSlot
|
||||
case FB_PD: result = f.readPalette()
|
||||
case FB_CTL: result = f.readCtl()
|
||||
default:
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (f *Framebuffer) write(value word, byteaddr word) (error) {
|
||||
addr := byteaddr & 0x7F
|
||||
switch addr {
|
||||
case FB_RA: f.readAddr = value
|
||||
case FB_WA: f.writeAddr = value
|
||||
case FB_IO: f.writeVmem(value)
|
||||
case FB_PS: f.paletteSlot = value
|
||||
case FB_PD: f.writePalette(value)
|
||||
case FB_CTL: f.writeCtl(value)
|
||||
default:
|
||||
}
|
||||
|
||||
idle(false)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Framebuffer) readVmem() word {
|
||||
result := f.vmem[f.readAddr & (VmemWords - 1)]
|
||||
f.readAddr += 1
|
||||
return result
|
||||
}
|
||||
|
||||
func (f *Framebuffer) writeVmem(value word) {
|
||||
vaddr := f.writeAddr & (VmemWords - 1)
|
||||
f.vmem[vaddr] = value
|
||||
|
||||
y := vaddr / WordsPerLine
|
||||
x := vaddr % WordsPerLine * PixelPerWord
|
||||
|
||||
for i := 0; i < PixelPerWord; i++ {
|
||||
pixel := (value & PixelMask) >> (VmemWidth - BitsPerPixel)
|
||||
value = value << BitsPerPixel
|
||||
|
||||
col := f.palette[pixel]
|
||||
//fmt.Printf("set pixel %v, %v\n", x,y)
|
||||
f.framebuffer.Set(int(x), int(y), col)
|
||||
x = x + 1
|
||||
}
|
||||
|
||||
f.writeAddr += 1
|
||||
}
|
||||
|
||||
func (f *Framebuffer) readPalette() word {
|
||||
return word(0)
|
||||
}
|
||||
|
||||
func (f *Framebuffer) writePalette(value word) {
|
||||
// 4 bits per color channel
|
||||
r := uint8((value & 0b111100000000) >> 8)
|
||||
g := uint8((value & 0b000011110000) >> 4)
|
||||
b := uint8((value & 0b000000001111) >> 0)
|
||||
|
||||
// scale to 0-255
|
||||
r = r << 4
|
||||
g = g << 4
|
||||
b = b << 4
|
||||
|
||||
f.palette[f.paletteSlot] = color.RGBA{r,g,b,0}
|
||||
}
|
||||
|
||||
func (f *Framebuffer) readCtl() word {
|
||||
if f.readCount == 0 {
|
||||
f.readCount = 1000
|
||||
return word(0)
|
||||
} else {
|
||||
f.readCount -= 1
|
||||
return word(1)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Framebuffer) writeCtl(value word) {
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue