프로젝트 | VHDL로 작성된 네번째 SoC |
---|---|
작가 | 리차드 제임스 하우 |
저작권 | 2013-2019 리처드 하우 |
특허 | MIT/LGPL |
이메일 | [email protected] |
이 프로젝트는 J1 CPU를 기반으로 Forth 실행에 맞춰진 소형 스택 컴퓨터를 구현합니다. 프로세서는 Verilog의 VHDL로 다시 작성되었으며 약간 확장되었습니다.
프로젝트의 목표는 다음과 같습니다.
세 가지 모두 완료되었습니다.
J1과 마찬가지로 H2 프로세서는 FORTH에 특히 적합한 명령어 세트를 실행하는 스택 기반 프로세서입니다.
현재 목표는 Xilinx Spartan-6 XC6LX16-CS324 FPGA를 갖춘 Nexys3 보드입니다. 이 보드의 수명이 다해가면 향후 새 보드가 목표가 될 것입니다. VHDL은 하드웨어 구성 요소가 명시적으로 인스턴스화되는 대신 추론되는 일반적인 방식으로 작성되므로 Nexys3 보드 구성 요소에 대한 인터페이스가 해당 보드의 주변 장치에 특정하더라도 코드의 이식성이 상당히 높아집니다.
하드웨어에서 진행 중인 프로젝트의 비디오는 여기에서 볼 수 있습니다.
SoC는 아래와 같이 C로 작성된 시뮬레이터를 사용하여 시뮬레이션할 수도 있습니다.
시스템 아키텍처는 다음과 같습니다.
프로젝트에서 사용되는 라이센스는 혼합되어 있으며 파일 단위입니다. 내 코드에는 MIT 라이센스를 사용하므로 원하는 대로 자유롭게 사용하세요. 사용되는 다른 라이센스는 LGPL 및 Apache 2.0 라이센스이며, 단일 모듈로 제한되어 있으므로 LGPL 코드에 대한 혐오감을 느끼는 경우 제거할 수 있습니다.
현재 사용할 수 있는 유일한 대상 보드는 Nexys3입니다. 보드가 현재 수명 종료 상태이므로 향후 변경되어야 합니다. 제가 지원하려고 하는 다음 보드는 후속 보드인 Nexys 4와 myStorm BlackIce(https://mystorm.uk/)입니다. myStorm 보드는 합성, 배치 및 경로, 비트 파일 생성을 위해 완전한 오픈 소스 도구 체인을 사용합니다.
빌드는 Debian Linux 버전 8에서 테스트되었습니다.
다음이 필요합니다.
하드웨어:
Xilinx ISE는 무료로 다운로드할 수 있지만 등록이 필요합니다. ISE가 경로에 있어야 합니다.
PATH=$PATH:/opt/Xilinx/14.7/ISE_DS/ISE/bin/lin64;
PATH=$PATH:/opt/Xilinx/14.7/ISE_DS/ISE/lib/lin64;
C 기반 툴체인을 만들려면 다음을 수행하세요.
make embed.hex
타겟 보드에 플래시할 수 있는 비트 파일을 만들려면:
make simulation synthesis implementation bitfile
비트파일을 타겟 보드에 업로드하려면:
make upload
"시뮬레이션 만들기"에 의해 생성된 파형을 보려면:
make viewer
C 기반 CLI 시뮬레이터는 다음을 사용하여 호출할 수 있습니다.
make run
H2 Forth 소스 파일 embed.fth를 어셈블하고 디버거가 활성화된 H2 시뮬레이터에서 어셈블된 개체 파일을 실행합니다. 그래픽 시뮬레이터는 다음을 사용하여 실행할 수 있습니다.
make gui-run
C 컴파일러뿐만 아니라 freeglut도 필요합니다.
원래 J1 프로젝트는 다음 위치에서 사용할 수 있습니다.
이 프로젝트는 원래 J1 코어를 대상으로 하며 eForth 구현을 제공합니다(J1 코어에 대한 메타 컴파일/교차 컴파일의 경우 Gforth를 사용하여 작성됨). 또한 C로 작성된 시스템에 대한 시뮬레이터도 제공합니다.
메타컴파일러의 기반이 되는 eForth 인터프리터는 다음 위치에서 찾을 수 있습니다.
H2 프로세서 및 관련 주변 장치는 이제 매우 안정적이지만 소스는 항상 레지스터 맵뿐만 아니라 명령 및 주변 장치의 작동 방식에 대한 최종 가이드입니다.
J1 CPU에는 다음과 같은 몇 가지 수정 사항이 있습니다.
H2 CPU는 J1 CPU와 매우 유사하게 작동하며, 이 프로세서를 더 잘 이해하기 위해 J1 PDF를 읽을 수 있습니다. 프로세서는 단일 클록 주기를 사용하는 명령이 있는 16비트입니다. 원시 Forth 단어의 대부분은 단일 주기에서도 실행될 수 있습니다. 주목할만한 예외 중 하나는 두 개의 명령으로 분할되는 저장("!")입니다.
CPU 내부에는 다음과 같은 상태가 있습니다.
H2 프로그램을 보유하는 블록 RAM에 로드하고 저장하면 가장 낮은 비트가 삭제되며, 다른 모든 메모리 작업은 더 낮은 비트를 사용합니다(예: 입력/출력 주변 장치에 대한 점프 및 로드 및 저장). 이는 응용 프로그램이 프로그램 RAM에 액세스할 때 문자 작업에 가장 낮은 비트를 사용할 수 있도록 하기 위한 것입니다.
명령어 세트는 다음과 같은 방식으로 디코딩됩니다.
+---------------------------------------------------------------+
| F | E | D | C | B | A | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+---------------------------------------------------------------+
| 1 | LITERAL VALUE |
+---------------------------------------------------------------+
| 0 | 0 | 0 | BRANCH TARGET ADDRESS |
+---------------------------------------------------------------+
| 0 | 0 | 1 | CONDITIONAL BRANCH TARGET ADDRESS |
+---------------------------------------------------------------+
| 0 | 1 | 0 | CALL TARGET ADDRESS |
+---------------------------------------------------------------+
| 0 | 1 | 1 | ALU OPERATION |T2N|T2R|N2A|R2P| RSTACK| DSTACK|
+---------------------------------------------------------------+
| F | E | D | C | B | A | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+---------------------------------------------------------------+
T : Top of data stack
N : Next on data stack
PC : Program Counter
LITERAL VALUES : push a value onto the data stack
CONDITIONAL : BRANCHS pop and test the T
CALLS : PC+1 onto the return stack
T2N : Move T to N
T2R : Move T to top of return stack
N2A : STORE T to memory location addressed by N
R2P : Move top of return stack to PC
RSTACK and DSTACK are signed values (twos compliment) that are
the stack delta (the amount to increment or decrement the stack
by for their respective stacks: return and data)
모든 ALU 작업은 T를 대체합니다.
값 | 작업 | 설명 |
---|---|---|
0 | 티 | 스택 상단 |
1 | N | T를 N으로 복사 |
2 | 티 + 엔 | 덧셈 |
3 | T&N | 비트별 AND |
4 | T 또는 N | 비트별 OR |
5 | 티^엔 | 비트별 XOR |
6 | ~티 | 비트 단위 반전 |
7 | 티 = N | 평등성 테스트 |
8 | N < T | 부호 있는 비교 |
9 | 아니 >> 티 | 논리적 오른쪽 시프트 |
10 | T - 1 | 감소 |
11 | 아르 자형 | 리턴 스택의 상단 |
12 | [티] | 주소에서 로드 |
13 | 아니 << 티 | 논리적 왼쪽 시프트 |
14 | 깊이 | 스택 깊이 |
15 | 누< T | 부호 없는 비교 |
16 | CPU 상태 설정 | 인터럽트 활성화 |
17 | CPU 상태 가져오기 | 인터럽트가 켜져 있나요? |
18 | 깊이 | 반환 깊이 stk |
19 | 0= | 티 == 0? |
20 | CPU ID | CPU 식별자 |
21 | 오자 | 내부 지시 |
'o' 접두사가 붙은 레지스터는 출력 레지스터이고, 'i' 접두사가 붙은 레지스터는 입력 레지스터입니다. 레지스터는 레지스터의 입력부와 출력부로 구분되며, 입력부와 출력 레지스터의 주소는 모든 경우에 서로 일치하지는 않습니다.
Nexys3 보드의 장치와 인터페이스하기 위해 VHDL SoC에 다음 주변 장치가 구현되었습니다.
SoC에는 활성화 또는 비활성화할 수 있는 제한된 인터럽트 세트도 있습니다.
출력 레지스터 맵:
등록하다 | 주소 | 설명 |
---|---|---|
oUart | 0x4000 | UART 레지스터 |
oVT100 | 0x4002 | VT100 터미널 쓰기 |
oLED | 0x4004 | LED 출력 |
oTimerCtrl | 0x4006 | 타이머 제어 |
oMemDout | 0x4008 | 메모리 데이터 출력 |
oMemControl | 0x400A | 메모리 제어/하이 주소 |
oMemAddr낮음 | 0x400C | 메모리 Lo 주소 |
o7세그LED | 0x400E | 4 x LED 7 세그먼트 디스플레이 |
oIrc마스크 | 0x4010 | CPU 인터럽트 마스크 |
oUartBaudTx | 0x4012 | UART Tx 보드 클럭 설정 |
oUartBaudRx | 0x4014 | UART Rx 보드 클럭 설정 |
입력 레지스터는 다음과 같습니다.
등록하다 | 주소 | 설명 |
---|---|---|
아이유아트 | 0x4000 | UART 레지스터 |
iVT100 | 0x4002 | 단말기 상태 및 PS/2 키보드 |
i스위치 | 0x4004 | 버튼 및 스위치 |
iTimerDin | 0x4006 | 현재 타이머 값 |
iMemDin | 0x4008 | 메모리 데이터 입력 |
레지스터에 대한 다음 설명을 순서대로 읽어야 하며 주변 장치의 작동 방식도 설명해야 합니다.
SoC에는 고정 전송 속도 및 형식(115200, 8비트, 1 정지 비트)을 갖는 UART가 있습니다. UART에는 RX 및 TX 채널 모두에 깊이 8의 FIFO가 있습니다. UART의 제어는 oUart와 iUart로 분할됩니다.
UART에 값을 쓰려면 TXDO에 데이터를 넣는 것과 함께 TXWE를 어설션합니다. FIFO 상태는 iUart 레지스터를 통해 분석할 수 있습니다.
UART에서 값을 읽으려면: iUart를 확인하여 FIFO에 데이터가 있는지 확인할 수 있습니다. oUart 레지스터에서 RXRE가 어설션되면 다음 클록 사이클에 데이터가 iUart 레지스터에 표시됩니다.
UART의 전송 속도는 VHDL 프로젝트를 다시 빌드하여 변경할 수 있으며, 비트 길이, 패리티 비트 및 정지 비트는 uart.vhd를 수정해야만 변경할 수 있습니다.
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| X | X |TXWE| X | X |RXRE| X | X | TXDO |
+-------------------------------------------------------------------------------+
TXWE: UART TX Write Enable
RXRE: UART RX Read Enable
TXDO: UART TX Data Output
VGA 텍스트 장치는 oVT100 레지스터에 기록하여 사용자가 대화할 수 있는 터미널을 에뮬레이트합니다. 이는 VT100 터미널 기능의 하위 집합을 지원합니다. 인터페이스는 동일한 사용 중 및 제어 신호를 사용하여 UART에 쓰는 것과 매우 유사하게 작동합니다. 입력은 보드에 있는 PS/2 키보드에서 가져오며 이는 UART의 RX 메커니즘처럼 작동합니다.
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| X | X |TXWE| X | X |RXRE| X | X | TXDO |
+-------------------------------------------------------------------------------+
TXWE: VT100 TX Write Enable
RXRE: UART RX Read Enable
TXDO: UART TX Data Output
Nexys3 보드에는 스위치 옆에 LED 뱅크가 있으며, 이러한 LED는 LEDO에 기록하여 켜거나(1) 끌 수 있습니다(0). 여기의 각 LED는 옆에 있는 스위치에 해당합니다.
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| X | X | X | X | X | X | X | X | LEDO |
+-------------------------------------------------------------------------------+
LEDO: LED Output
타이머는 oTimerCtrl 레지스터로 제어할 수 있으며 100MHz에서 실행되는 13비트 타이머이며 선택적으로 인터럽트를 생성할 수 있고 현재 타이머 내부 카운트를 iTimerDin 레지스터로 다시 읽을 수 있습니다.
타이머는 TE 비트가 지정되면 카운트하고 타이머가 TCMP 값에 도달하면 순환하며 선택적으로 INTE를 지정하여 인터럽트를 생성할 수 있습니다. 이는 또한 타이머에서 나오고 보드의 핀으로 라우팅되는 Q 및 NQ 라인을 토글합니다(핀에 대한 제약 조건 파일 top.ucf 참조).
타이머는 RST에 기록하여 재설정할 수 있습니다.
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| TE | RST|INTE| TCMP |
+-------------------------------------------------------------------------------+
TE: Timer Enable
RST: Timer Reset
INTE: Interrupt Enable
TCMP: Timer Compare Value
H2 코어에는 인터럽트 메커니즘이 있으며, 인터럽트는 명령어를 통해 활성화하거나 비활성화해야 합니다. 각 인터럽트는 IMSK의 비트로 마스크되어 해당 특정 인터럽트를 활성화할 수 있습니다. IMSK 비트의 '1'은 특정 인터럽트를 활성화하며, CPU 내에서 인터럽트가 활성화되면 해당 인터럽트가 CPU로 전달됩니다.
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| X | X | X | X | X | X | X | X | IMSK |
+-------------------------------------------------------------------------------+
IMSK: Interrupt Mask
이 레지스터는 전송 전용 보드 및 샘플 클럭 주파수를 설정하는 데 사용됩니다.
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| BTXC |
+-------------------------------------------------------------------------------+
BTXC: Baud Clock Settings
이 레지스터는 수신 전용 보드 및 샘플 클럭 주파수를 설정하는 데 사용됩니다.
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| BRXC |
+-------------------------------------------------------------------------------+
BRXC: Baud Clock Settings
oMemControl에서 WE(쓰기 활성화)가 발행되면 선택한 주소로 출력할 데이터입니다.
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| Data Ouput |
+-------------------------------------------------------------------------------+
이 레지스터에는 Nexys3 보드의 온보드 메모리에 대한 제어 레지스터가 포함되어 있습니다. 보드에는 3개의 메모리 장치, 2개의 비휘발성 메모리 장치 및 휘발성 RAM 기반 장치가 포함되어 있습니다. 간단한 SRAM 인터페이스(휘발성 M45W8MW16 1개, 비휘발성 1개 - NP8P128A13T1760E)를 통해 액세스할 수 있는 두 장치는 모두 액세스 가능하며, 세 번째는 SPI 기반 메모리 장치인 NP5Q128A13ESFC0E이며 현재는 액세스할 수 없습니다.
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| OE | WE | RST|WAIT| RCS| FCS| Address Hi |
+-------------------------------------------------------------------------------+
OE: Output Enable - enable reading from current address into iMemDin
WE: Write Enable - enable writing oMemDout into ram at current address
RST: Reset the Flash memory controller
RCS: RAM Chip Select, Enable Volatile Memory
FCS: Flash Chip Select, Enable Non-Volatile Memory
Address Hi: High Bits of RAM address
OE와 WE는 상호 배타적입니다. 둘 다 설정하면 아무런 효과가 없습니다.
메모리 컨트롤러는 현재 개발 중이므로 이에 대한 인터페이스가 변경될 수 있습니다.
이것은 RAM의 하위 주소 비트입니다.
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| Address Lo |
+-------------------------------------------------------------------------------+
Nexys3 보드에는 숫자 출력에 사용할 수 있는 소수점(실제로는 8세그먼트)이 있는 7세그먼트 디스플레이 뱅크가 있습니다. LED 세그먼트는 직접 주소를 지정할 수 없습니다. 대신 L8SD에 저장된 값은 16진수 표시 값(또는 BCD 값이지만 이를 위해서는 SoC를 재생성하고 VHDL에서 일반 값을 수정해야 함)에 매핑됩니다.
값 '0'은 LED 세그먼트에 표시된 0에 해당하고 '15'는 'F' 등에 해당합니다.
연속으로 4개의 디스플레이가 있습니다.
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| L7SD0 | L7SD1 | L7SD2 | L7SD3 |
+-------------------------------------------------------------------------------+
L7SD0: LED 7 Segment Display (leftmost display)
L7SD1: LED 7 Segment Display
L7SD2: LED 7 Segment Display
L7SD3: LED 7 Segment Display (right most display)
iUart 레지스터는 oUart 레지스터와 함께 작동합니다. 바이트의 전송과 수신을 모두 버퍼링하는 FIFO의 상태는 iUart 레지스터와 수신된 바이트에서 확인할 수 있습니다.
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| X | X | X |TFFL|TFEM| X |RFFL|RFEM| RXDI |
+-------------------------------------------------------------------------------+
TFFL: UART TX FIFO Full
TFEM: UART TX FIFO Empty
RFFL: UART RX FIFO Full
RFEM: UART RX FIFO Empty
RXDI: UART RX Data Input
iVT100 레지스터는 oVT100 레지스터와 함께 작동합니다. 바이트 전송 및 수신을 모두 버퍼링하는 FIFO의 상태는 수신된 바이트뿐만 아니라 iVT100 레지스터에서 사용할 수 있습니다. iUart/oUart 레지스터와 동일하게 작동합니다.
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| X | X | X |TFFL|TFEM| X |RFFL|RFEM| 0 | ACHR |
+-------------------------------------------------------------------------------+
TFFL: VGA VT100 TX FIFO Full
TFEM: VGA VT100 TX FIFO Empty
RFFL: PS2 VT100 RX FIFO Full
RFEM: PS2 VT100 RX FIFO Empty
ACHR: New character available on PS2 Keyboard
이 레지스터에는 타이머 카운터의 현재 값이 포함되어 있습니다.
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| X | X | X | TCNT |
+-------------------------------------------------------------------------------+
TCNT: Timer Counter Value
iSwitches에는 여러 소스의 입력 라인이 포함되어 있습니다. 버튼(BUP, BDWN, BLFT, BRGH 및 BCNT)은 Nexys3 보드의 D-Pad에 해당합니다. 스위치(TSWI)는 oLeds에서 언급된 스위치이며 각 스위치 옆에는 LED가 있습니다.
스위치와 버튼은 이미 하드웨어에서 디바운싱되었으므로 이러한 레지스터에서 읽어온 후에는 추가 처리가 필요하지 않습니다.
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| X | X | X | BUP|BDWN|BLFT|BRGH|BCNT| TSWI |
+-------------------------------------------------------------------------------+
BUP: Button Up
BDWN: Button Down
BLFT: Button Left
BRGH: Button Right
BCNT: Button Center
TSWI: Two Position Switches
oMemControl 및 oMemAddrLow로 인덱싱된 SRAM 또는 플래시의 메모리 입력입니다. 플래시에서 읽을 때 이는 실제로 상태 정보이거나 쿼리 테이블의 정보일 수 있습니다.
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| Data Input |
+-------------------------------------------------------------------------------+
다음 인터럽트 서비스 루틴이 정의됩니다:
이름 | 숫자 | 설명 |
---|---|---|
isr없음 | 0 | 사용되지 않음 |
isrRxFifo가 비어 있지 않음 | 1 | UART RX FIFO가 비어 있지 않습니다. |
isrRxFifo전체 | 2 | UART RX FIFI가 꽉 찼습니다. |
isrTxFifo가 비어 있지 않음 | 3 | UART TX FIFO가 비어 있지 않습니다. |
isrTxFifo가득함 | 4 | UART TX FIFO가 꽉 찼습니다. |
isrKbdNew | 5 | 새로운 PS/2 키보드 캐릭터 |
isrTimer | 6 | 타이머 카운터 |
isrDPad버튼 | 7 | 모든 D패드 버튼 상태 변경 |
인터럽트가 발생하고 프로세서 내에서 인터럽트가 활성화되면 메모리 위치에 대한 호출이 수행됩니다. 위치는 ISR 번호와 동일합니다. 예를 들어, 숫자가 '4'인 ISR은 메모리 내 '4' 위치에 대한 호출(점프 아님)을 수행합니다.
인터럽트는 작동되기 전에 최소 4~5사이클의 대기 시간을 가지며, 인터럽트 요청 핸들러에는 2~3사이클 지연이 있습니다. 그런 다음 메모리의 ISR 위치에 대한 호출이 수행되어야 합니다. ISR 자체를 구현하는 단어입니다.
두 개의 인터럽트가 동시에 발생하면 가장 낮은 인터럽트 번호부터 가장 높은 인터럽트 번호까지 처리됩니다.
처리되지 않은 동일한 번호의 인터럽트가 발생하면 인터럽트가 손실됩니다.
H2용 디스어셈블러 및 C 기반 시뮬레이터는 단일 프로그램에 있습니다(h2.c 참조). 이 시뮬레이터는 VHDL 테스트 벤치 tb.vhd를 보완하며 이를 대체하지 않습니다. 메타 컴파일러는 eForth 인터프리터 위에서 실행되며 embed.c 및 embed.blk 파일에 포함되어 있습니다. 메타 컴파일러(크로스 컴파일러의 Forth 용어)는 대상에서 실행되는 eForth 이미지를 생성하는 데 사용되는 Forth 프로그램입니다.
툴체인은 현재 유동적이며, 앞으로는 자체 호스팅 생성이라는 장기적 목표를 가지고 Embed Virtual Machine을 H2 CPU와 더 유사한 것으로 변경하는 것과 함께 h2.c와 embed.c 사이에 더 많은 통합이 있을 것입니다. 체계.
둘 다 빌드하려면 C 컴파일러가 필요합니다. 빌드 대상 "h2"는 실행 파일 h2를 빌드하고 "embed"는 메타 컴파일러를 빌드합니다.
make h2 embed
그리고 make 대상을 사용하여 소스 파일 embed.fth에서 실행할 수 있습니다.
make run
make 파일은 필요하지 않습니다.
Linux:
cc -std=c99 h2.c -o h2 # To build the h2 executable
cc -std=c99 embed.c -o embed # To build the embed VM executable
./embed embed.blk embed.hex embed.fth # Create the target eForth image
./h2 -h # For a list of options
./h2 -r embed.hex # Run the assembled file
Windows:
gcc -std=c99 h2.c -o h2.exe # Builds the h2.exe executable
gcc -std=c99 embed.c -o embed.exe # Builds the embed.exe executable
embed.exe embed.blk embed.hex embed.fth # Create the target eForth iamge
h2.exe -h # For a list of options
h2.exe -r embed.hex # Run the assembled file
사용 가능한 명령줄 옵션 목록:
- stop processing options, following arguments are files
-h print a help message and exit
-v increase logging level
-d disassemble input files (default)
-D full disassembly of input files
-T Enter debug mode when running simulation
-r run hex file
-L # load symbol file
-s # number of steps to run simulation (0 = forever)
-n # specify NVRAM block file (default is nvram.blk)
file* file to process
이 프로그램은 MIT 라이선스로 배포되므로 자유롭게 사용하고 원하는 대로 수정하세요. 최소한의 수정만으로 원래 J1 코어용 프로그램을 조립할 수 있어야 합니다.
메타 컴파일러는 내장된 가상 머신 위에서 실행됩니다. 이는 원래 H2 CPU에서 파생된 16비트 가상 머신입니다. 이 프로젝트에는 eForth 이미지가 수정 사항을 통해 새로운 eForth 이미지를 생성할 수 있도록 하는 메타 컴파일 체계가 포함되어 있습니다. 해당 시스템은 C로 작성된 크로스 컴파일러를 대체하여 H2의 첫 번째 이미지를 생성할 수 있는 H2와 함께 사용하도록 조정되었습니다.
메타 컴파일러는 일반적인 Forth 프로그램이며, embed.fth에 포함되어 있습니다. 그런 다음 메타 컴파일러 Forth 프로그램을 사용하여 H2 대상에서 실행할 수 있는 eForth 이미지를 구축합니다.
Forth의 메타컴파일에 대한 자세한 내용은 다음을 참조하세요.
디스어셈블러는 16비트 16진수로 구성된 어셈블된 프로그램이 포함된 텍스트 파일을 사용합니다. 그런 다음 지침을 분해하려고 시도합니다. 또한 어셈블러에서 생성할 수 있는 기호 파일을 제공하고 점프 및 호출이 가리키는 위치를 찾으려고 시도할 수도 있습니다.
디스어셈블러는 GTKwave가 호출하는 tcl 스크립트에 의해 사용되며, H2의 명령어 추적을 일련의 숫자에서 이들이 나타내는 명령어 및 분기 대상으로 바꿉니다. 이렇게 하면 VHDL 디버깅이 훨씬 쉬워집니다.
보라색 흔적은 분해된 지침을 보여줍니다.
C의 시뮬레이터는 H2 코어와 대부분의 SoC를 구현합니다. 시뮬레이터의 IO는 주기가 정확하지 않지만 하드웨어 작동 방식과 매우 유사한 결과로 프로그램을 실행하고 디버깅하는 데 사용할 수 있습니다. 이는 FPGA를 플래시하는 데 사용되는 비트 파일을 다시 빌드하는 것보다 훨씬 빠릅니다.
시뮬레이터에는 DOS에서 사용할 수 있는 DEBUG.COM 프로그램과 유사하게 설계된 디버거도 포함되어 있습니다. 디버거는 메모리 섹션을 분해하고, 주변 장치의 상태를 검사하고, 메모리 섹션을 화면에 덤프하는 데 사용할 수 있습니다. 또한 중단점을 설정하고, 단일 단계를 수행하고, 중단점에 도달할 때까지 코드를 실행하는 데에도 사용할 수 있습니다.
디버거를 실행하려면 16진수 파일이나 소스 파일을 제공해야 합니다.
# -T turns debugging mode on
./h2 -T -r file.hex # Run simulator
두 가지 작동 모드 모두 기호 파일을 사용하여 확장할 수 있습니다. 이 파일에는 조립된 코어와 함께 변수, 라벨 및 기능이 있는 위치가 나열되어 있습니다.
"-T" 옵션이 주어지면 시뮬레이션이 실행되기 전에 디버그 모드로 들어갑니다. 프롬프트가 나타나고 명령줄은 다음과 같아야 합니다.
$ ./h2 -T -R h2.fth
Debugger running, type 'h' for a list of command
debug>
중단점은 기호로 또는 프로그램 위치별로 설정할 수 있으며 'b' 명령은 중단점을 설정하는 데 사용됩니다.
숫자는 8진수(숫자 앞에 '0'이 붙음), 16진수(접두사가 '0x') 또는 10진수로 입력될 수 있습니다. 예를 들어 다음 세 가지 디버그 명령은 모두 동일한 위치에 중단점을 설정합니다.
debug> b 16
debug> b 0x10
debug> b 020
'k'는 설정된 현재 중단점을 나열하는 데 사용할 수 있습니다.
debug> k
0x0010
이는 "key?" 함수가 실행될 때 중단점을 설정합니다. 호출됩니다 :
debug> b key?
함수와 레이블은 모두 정지될 수 있습니다. 이를 위해서는 명령줄에 기호 파일을 지정하거나 16진수 파일이 아닌 소스 파일에서 사용하기 위해 어셈블하고 실행해야 합니다. 기호 파일은 소스 또는 16진수 파일에서 사용할 수 있습니다.
단일 단계에는 's' 명령을 사용할 수 있지만 추적이 꺼져 있으면 많은 일이 발생하지 않습니다(추적은 기본적으로 꺼져 있습니다). 't' 명령을 사용하여 추적을 켜거나 끌 수 있습니다.
debug> s
debug> s
debug> t
trace on
debug> s
0001: pc(089a) inst(4889) sp(0) rp(0) tos(0000) r(0000) call 889 init
debug> s
0002: pc(0889) inst(807a) sp(0) rp(1) tos(0000) r(089b) 7a
debug> s
0003: pc(088a) inst(e004) sp(1) rp(1) tos(007a) r(089b) 6004
'c' 또는 계속 명령을 실행할 때 추적을 끄는 것이 좋습니다.
'.' 명령을 사용하여 H2 코어 내부 상태를 표시할 수 있습니다.
debug> .
Return Stack:
0000: 0000 08aa 0883 017b 0000 031b 0000 ffb0 0000 02eb ffb5 0210 0167 0167
0167 0167
0010: 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0000 0000
Variable Stack:
tos: 0000
0001: 0000 0000 0000 0001 0004 0005 0000 ffb0 0000 0000 0000 0000 0000 0000
0000 0000
0011: 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0000 0000
pc: 0538
rp: 0001
dp: 0000
ie: false
그리고 'p' 명령을 사용하여 시뮬레이션된 주변 장치의 상태를 표시할 수 있습니다.
debug> p
LEDS: 00
VGA Cursor: 0005
VGA Control: 007a
Timer Control: 8032
Timer: 001b
IRC Mask: 0000
UART Input: 6c
LED 7seg: 0005
Switches: 00
LFSR: 40ba
Waiting: false
전체 명령 목록을 보려면 'h' 명령을 사용하세요.
디버그 모드로 들어가는 다른 방법으로는 ".break" 어셈블러 지시문을 소스 코드에 넣는 것(이것은 어셈블 및 실행 명령이 16진수 파일이 아닌 소스 파일에서 사용되는 경우에만 작동함), 시뮬레이터가 실행될 때 이스케이프 문자를 누르는 것 등이 있습니다. 시뮬레이션된 UART 또는 PS/2 키보드를 통해 데이터 읽기를 시도합니다(이스케이프는 여전히 시뮬레이터에 전달되지만 디버그 모드도 활성화됩니다).
Linux 및 Windows에서 별도의 프로그램을 컴파일하고 테스트할 수 있습니다. 이는 SoC와 인터페이스하는 Nexys3 보드 주변 장치를 시뮬레이션하지만 명령줄 유틸리티와는 달리 그래픽 환경을 제공합니다. 장치와 상호 작용하고 장치가 수행하는 작업을 확인하는 것이 더 쉽지만 디버깅 세션은 덜 제어됩니다. 무료 과잉이 필요합니다.
다음은 GUI 시뮬레이터에서 실행 중인 세션의 이미지입니다.
건물은 다음과 같이 할 수 있습니다
make gui
그리고 실행 중:
make gui-run
또는:
./gui h2.hex (on Linux)
gui.exe h2.hex (on Windows)
Linux 빌드는 시스템에 무료 글루용 개발 패키지가 설치된 경우 작동해야 하며, Windows 빌드에는 빌드 시스템 변경 및/또는 컴파일러, 라이브러리 및 헤더의 수동 설치가 필요할 수 있습니다.
현재 키 맵은 다음과 같습니다.
Up Activate Up D-Pad Button, Release turns off
Down Activate Down D-Pad Button, Release turns off
Left Activate Left D-Pad Button, Release turns off
Right Activate Right D-Pad Button, Release turns off
F1 - F8 Toggle Switch On/Off, F1 is left most, F8 Right Most
F11 Toggle UART/PS2 Keyboard Input
F12 Toggle Debugging Information
Escape Quit simulator
다른 모든 키보드 키는 UART 또는 PS/2 키보드 입력으로 리디렉션됩니다.
스위치와 D 패드 버튼을 클릭하여 켤 수 있으며, 스위치는 왼쪽 클릭으로 켜지고 오른쪽 클릭으로 꺼집니다. D-패드 버튼은 그 위에서 클릭하면 켜지고, 화면 아무 곳에서나 키를 놓으면 꺼집니다.
이 시스템에 사용되는 VHDL 구성 요소는 다양한 도구 체인 및 공급업체에서 재사용 및 이식이 가능하도록 설계되었습니다. 블록 RAM과 같은 하드웨어 구성 요소는 추론되며 명시적으로 인스턴스화되지 않습니다. 또한 구성 요소는 가능한 한 일반적으로 만들어졌으며 대부분은 선택 가능한 너비를 갖습니다. 이는 극단적인 방법이지만 안타깝게도 많은 공급업체가 여전히 VHDL-2008 표준을 지원하지 않습니다.
파일 | 특허 | 작가 | 설명 |
---|---|---|---|
util.vhd | MIT | 리차드 J 하우 | 일반 구성 요소 모음 |
h2.vhd | MIT | 리차드 J 하우 | H2 네 번째 CPU 코어 |
uart.vhd | MIT | 리차드 J 하우 | UART TX/RX(런타임 사용자 정의 가능) |
vga.vhd | LGPL 3.0 | 하비에르 V 가르시아 | 텍스트 모드 VGA 80x40 디스플레이 |
리차드 J 하우 | (및 VT100 터미널 에뮬레이터) | ||
kbd.vhd | ??? | 스콧 라슨 | PS/2 키보드 |
어셈블러로 사용되는 유사 Forth 유사 언어는 위에 설명되어 있으며 실제로 Forth 코어에서 실행되는 애플리케이션은 그 자체로 Forth 인터프리터입니다. 이 섹션에서는 H2 Core에서 실행되는 Forth 인터프리터에 대해 설명하며, 이는 embed.fth에 포함되어 있습니다.
할 일:
이 프로젝트 전체에는 여러 언어가 사용되며, 모두 서로 근본적으로 다르며 고유한 코딩 표준 및 스타일 가이드 세트가 필요합니다.
일반적인 신호 이름:
clk - The system clock
rst - A reset signal for the module
we - Write Enable
re - Read Enable
di - Data In
din - Data In
do - Data Out
dout - Data Out
control - Generally an input to a register, the documentation
for the module will need to be consulted to find out
what each bit means
signal_we - The write enable for 'signal'
signal_i - This is an input signal
signal_o - This is an output signal
일반적으로 "_i" 및 "_o" 접미사는 사용되지 않으며, 모듈은 짧게 유지되고 이름은 의미가 분명하도록 선택됩니다. 프로젝트가 성장하면 이 규칙을 다시 검토할 수 있습니다.
구성 요소는 다음을 수행해야 합니다.
constant N: positive := 4;
signal a: std_logic_vector(N - 1 downto 0) := (others => '1');
대신:
signal a: std_logic_vector(3 downto 0) := x"F";
스타일 규칙은 다음과 같습니다.
형식 지정 지침의 예는 간단한 임의 너비 레지스터를 설명합니다.
-- Lots of comments about what the unit does should go
-- here. Describe the waveforms, states and use ASCII
-- art where possible.
library ieee, work;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all; -- numeric_std not std_logic_arith
entity reg is -- generic and port indented one tab, their parameters two
generic (
N: positive); -- Generic parameters make for a generic component
port (
clk: in std_logic; -- standard signal names
rst: in std_logic; --
we: in std_logic;
di: in std_logic_vector(N - 1 downto 0);
do: out std_logic_vector(N - 1 downto 0)); -- note the position of ");
end entity; -- "end entity", not "end reg"
architecture rtl of reg is
signal r_c, r_n: std_logic_vector(N - 1 downto 0) := (others => '0');
begin
do <= r_c;
process(rst, clk)
begin
if rst = '1' then -- asynchronous reset
r_c <= (others => '0');
elsif rising_edge(clk) then -- rising edge, not "clk'event and clk = '1'"
r_c <= r_n;
end if;
end process;
process(r_c, di, we)
begin
r_n <= r_c;
if we = '1' then
r_n <= di;
end if;
end process;
end; -- "end" or "end architecture"
이 프로젝트에는 H2 코어용 툴 체인을 만들고 시스템을 시뮬레이션하는 데 사용되는 C 코드가 상당히 많습니다.
여기에 있는 C 코드에는 그다지 놀라운 것이 없으므로 일부 예외를 처리해야 합니다.
static const char *alu_op_to_string(uint16_t instruction) {
/* notice also that the 'case' clauses are inline with the
* switch selector */
switch (ALU_OP(instruction)) {
case ALU_OP_T: return "T";
case ALU_OP_N: return "N";
case ALU_OP_T_PLUS_N: return "T+N";
case ALU_OP_T_AND_N: return "T&N";
case ALU_OP_T_OR_N: return "T|N";
case ALU_OP_T_XOR_N: return "T^N";
case ALU_OP_T_INVERT: return "~T";
case ALU_OP_T_EQUAL_N: return "N=T";
case ALU_OP_N_LESS_T: return "T>N";
case ALU_OP_N_RSHIFT_T: return "N>>T";
case ALU_OP_T_DECREMENT: return "T-1";
case ALU_OP_R: return "R";
case ALU_OP_T_LOAD: return "[T]";
case ALU_OP_N_LSHIFT_T: return "N<N";
case ALU_OP_ENABLE_INTERRUPTS: return "seti";
case ALU_OP_INTERRUPTS_ENABLED: return "iset?";
case ALU_OP_RDEPTH: return "rdepth";
case ALU_OP_T_EQUAL_0: return "0=";
case ALU_OP_CPU_ID: return "cpu-id";
default: return "unknown";
}
}
if (foo)
bar();
else
baz();
picocom --omap delbs -b 115200 -e b /dev/ttyUSB1