Squelette du projet
This commit is contained in:
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
RTL
|
||||||
|
out
|
||||||
|
build
|
||||||
|
mill*
|
||||||
|
.prog*
|
||||||
|
bench/mem
|
||||||
1
.mill-jvm-opts
Normal file
1
.mill-jvm-opts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
-Dchisel.project.root=${PWD}
|
||||||
1
.mill-version
Normal file
1
.mill-version
Normal file
@@ -0,0 +1 @@
|
|||||||
|
0.12.5
|
||||||
186
Makefile
Normal file
186
Makefile
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
COMMON_DIR := common
|
||||||
|
|
||||||
|
TOCLEAN = $(filter %log %.jou %.rpt %.mmi %.dcp %.csv\
|
||||||
|
%.wdb vivado% updatemem% usage_statistics_webtalk%,$(wildcard *))
|
||||||
|
TOCLEAN += $(wildcard _*) .Xil out RTL build download.bit
|
||||||
|
|
||||||
|
# macro de verbosité
|
||||||
|
VERB := 0
|
||||||
|
q = $(if $(filter 1, $(VERB)),$1,\
|
||||||
|
$(if $2,@echo $2 && $1 > $3 2>&1, @$1))
|
||||||
|
|
||||||
|
# macro de vérification de variables d'environnement
|
||||||
|
check_vars = $(foreach v, $1, $(if $(filter undefined, $(origin $v)),\
|
||||||
|
if [ "$v" = "XILINX_VIVADO" ]; then \
|
||||||
|
echo "⚠️ Tapez : source /bigsoft/Xilinx/Vivado/2019.1/settings64.sh"; \
|
||||||
|
exit 1; \
|
||||||
|
else \
|
||||||
|
echo "⚠️ ⚡⚡ Variable d'environnement $v manquante ⚡⚡⚠️ "; \
|
||||||
|
$(MAKE) --no-print-directory help; \
|
||||||
|
exit 1; \
|
||||||
|
fi;))
|
||||||
|
|
||||||
|
|
||||||
|
all: help
|
||||||
|
|
||||||
|
help:
|
||||||
|
@awk 'BEGIN {FS = ":.*##!"; printf "Usage: make \033[32m<commande>\033[0m \
|
||||||
|
[PROG=<nom du fichier de test sans extension>] \
|
||||||
|
[CYCLES=durée de la simulation] [VERB=1 pour la verbosité]\
|
||||||
|
\nCommandes par \033[36mcatégories :\n"} \
|
||||||
|
/^[a-zA-Z0-9_-]+:.*##!/ { printf " \033[32m%-15s\033[0m %s\n", $$1, $$2 } \
|
||||||
|
/^##@/ { printf "\n\033[36m%s\033[0m\n", substr($$0, 5) }' $(MAKEFILE_LIST)
|
||||||
|
|
||||||
|
|
||||||
|
.PHONY: synthese fpga clean compile autotest simulation
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
|
||||||
|
## Partie Compilation
|
||||||
|
|
||||||
|
BENCH_DIR := bench
|
||||||
|
MEM_DIR := $(BENCH_DIR)/mem
|
||||||
|
SRC_SW := $(wildcard $(BENCH_DIR)/*.s)
|
||||||
|
SRC_SW += $(wildcard $(BENCH_DIR)/*.c) # Contient tous les .s et .c du projet
|
||||||
|
MEM = $(addprefix $(MEM_DIR)/, $(addsuffix .mem, $(basename $(notdir $(SRC_SW))))) # Tous les .mem du projet
|
||||||
|
PREFIX := $(shell command -v riscv64-unknown-elf-gcc >/dev/null 2>&1 && echo riscv64-unknown-elf- || echo /matieres/3MMFMN/riscv32/bin/riscv32-unknown-elf-)
|
||||||
|
CC := $(PREFIX)gcc
|
||||||
|
OBJDUMP := $(PREFIX)objdump
|
||||||
|
OBTOMEM := common/objtomem.awk
|
||||||
|
|
||||||
|
## Flags
|
||||||
|
ASFLAGS :=-march=rv32i -mabi=ilp32 -ffreestanding -nostdlib -T $(BENCH_DIR)/link.ld
|
||||||
|
CFLAGS :=$(ASFLAGS)
|
||||||
|
ELFFLAGS :=$(ASFLAGS) $(MEM_DIR)/crt.o -Os -fno-unroll-loops
|
||||||
|
ODFLAGS :=-j .text -j .rodata -j .data -s
|
||||||
|
|
||||||
|
.PRECIOUS: $(MEM_DIR)/%.elf
|
||||||
|
|
||||||
|
compile: $(MEM)
|
||||||
|
$(MEM_DIR):
|
||||||
|
@mkdir -p $@
|
||||||
|
$(MEM_DIR)/crt.o: $(BENCH_DIR)/crt.S |$(MEM_DIR)
|
||||||
|
$(call q,\
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@ , " Compilation crt",$@.log)
|
||||||
|
$(MEM_DIR)/%.elf: $(BENCH_DIR)/%.c $(MEM_DIR)/crt.o|$(MEM_DIR)
|
||||||
|
$(call q,\
|
||||||
|
$(CC) $(ELFFLAGS) $< -o $@ , " Compilation $<",$@.log)
|
||||||
|
$(MEM_DIR)/%.elf: $(BENCH_DIR)/%.s |$(MEM_DIR)
|
||||||
|
$(call q,\
|
||||||
|
$(CC) $(CFLAGS) -o $@ $<, " Compilation $<",$@.log)
|
||||||
|
$(MEM_DIR)/%.mem: $(MEM_DIR)/%.elf |$(MEM_DIR)
|
||||||
|
$(call q,\
|
||||||
|
$(OBJDUMP) $(ODFLAGS) $< | awk -f $(OBTOMEM) | awk -f common/fix_mem.awk," Génération de $@",$@)
|
||||||
|
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
|
||||||
|
##@ Simulation
|
||||||
|
|
||||||
|
TOP := ZzTop
|
||||||
|
TOP_REP := projet
|
||||||
|
PATH_SRC := src/main/scala/$(TOP_REP)
|
||||||
|
TOP_TEST := $(addsuffix Spec, $(TOP))
|
||||||
|
|
||||||
|
CYCLES=500
|
||||||
|
# Pour filtrer les tests dans une simulation
|
||||||
|
ifdef PROG
|
||||||
|
FILTRE_TEST:= -- -z "$(PROG)"
|
||||||
|
endif
|
||||||
|
|
||||||
|
mill:
|
||||||
|
@curl -L https://raw.githubusercontent.com/lefou/millw/0.4.11/millw > mill
|
||||||
|
@chmod +x mill
|
||||||
|
|
||||||
|
autotest: mill compile ##! Lance la simulation automatique pour tous les tests ou juste celui fourni dans PROG
|
||||||
|
@PROOT=$(PWD) ./mill TPchisel.test.testOnly $(TOP_REP).$(TOP_TEST) $(FILTRE_TEST)
|
||||||
|
|
||||||
|
simulation: compile ##! Lance gtkwave sur le test fourni dans PROG
|
||||||
|
@$(call check_vars,PROG)
|
||||||
|
@PROOT=$(PWD) PROG=$(PROG) CYCLES=$(CYCLES) ./mill TPchisel.test.testOnly $(TOP_REP).$(TOP_TEST) -- -DemitVcd=1 -z "gtkwave"
|
||||||
|
@gtkwave -a common/config.gtkw ./build/chiselsim/ZzTopSpec/Simulation-pour-gtkwave/workdir-verilator/trace.vcd
|
||||||
|
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
|
||||||
|
##@ Passage sur carte
|
||||||
|
|
||||||
|
SRC := $(wildcard $(PATH_SRC)/*.scala)
|
||||||
|
SRC += $(wildcard src/main/resources/vsrc/*.v)
|
||||||
|
FPGA := xc7z010-clg400-1
|
||||||
|
SRC_SV := RTL/$(TOP_REP).$(TOP)/$(TOP).sv
|
||||||
|
|
||||||
|
|
||||||
|
XILINX_PREFIX := $(XILINX_VIVADO)/bin/
|
||||||
|
MEM_FILE:= $(MEM_DIR)/$(PROG).mem
|
||||||
|
MMI_FILE:=$(TOP).mmi
|
||||||
|
MEM_ID=$(shell grep -oP 'InstPath="\K[^"]+' $(MMI_FILE))
|
||||||
|
PARAM = $(MEM_FILE) false # Variable pour passer des parametres au générateur SV
|
||||||
|
|
||||||
|
.PRECIOUS: golden.bit
|
||||||
|
# Créer un hash unique de la variable
|
||||||
|
PROG_HASH := $(shell echo "$(PROG)" | md5sum | cut -d' ' -f1)
|
||||||
|
|
||||||
|
RTL: $(SRC_SV) ##! Génération du SystemVerilog
|
||||||
|
|
||||||
|
$(SRC_SV): mill $(SRC)
|
||||||
|
$(call q, ./mill TPchisel.runMain common.EmitModule $(TOP_REP).$(TOP) $(PARAM) \
|
||||||
|
, " Génération du SystemVerilog",/dev/null)
|
||||||
|
|
||||||
|
$(TOP)_utilization.rpt $(TOP)_timing.rpt $(TOP)_summary.rpt: $(COMMON_DIR)/synthese.vivado.tcl RTL
|
||||||
|
@$(call check_vars,XILINX_VIVADO)
|
||||||
|
$(call q, \
|
||||||
|
$(XILINX_PREFIX)vivado -nolog -nojournal -mode batch -source $< -tclargs $(FPGA) $(TOP) $(TOP_REP)\
|
||||||
|
, " Synthese",$@.log)
|
||||||
|
|
||||||
|
synthese: $(TOP)_utilization.rpt $(TOP)_timing.rpt $(TOP)_summary.rpt ##! Réalise la synthèse grossière pour évaluer les performances
|
||||||
|
@echo "Rapport d'utilisation disponible dans $(TOP)_utilization.rpt"
|
||||||
|
@echo "Rapport de timing disponible dans $(TOP)_timing.rpt"
|
||||||
|
@python3 $(COMMON_DIR)/parse_report.py $^
|
||||||
|
|
||||||
|
golden.bit: $(COMMON_DIR)/bitstream.vivado.tcl $(COMMON_DIR)/$(TOP).xdc RTL
|
||||||
|
@$(call check_vars,XILINX_VIVADO PROG)
|
||||||
|
$(call q, \
|
||||||
|
$(XILINX_PREFIX)vivado -nolog -nojournal -mode batch -source $< -tclargs $(FPGA) $(TOP) $(TOP_REP) $@ $(MEM_FILE) \
|
||||||
|
, " Génération du bitstream",$@.log)
|
||||||
|
|
||||||
|
.prog_$(PROG_HASH):
|
||||||
|
@rm -f .prog_* # Supprimer les anciens marqueurs
|
||||||
|
@touch $@
|
||||||
|
|
||||||
|
download.bit: golden.bit $(MEM_FILE) $(MMI_FILE) .prog_$(PROG_HASH)
|
||||||
|
@$(call check_vars,XILINX_VIVADO PROG)
|
||||||
|
$(call q, $(XILINX_PREFIX)updatemem -debug -force --meminfo $(MMI_FILE) --data $(MEM_FILE) --proc $(MEM_ID) --bit $< --out $@\
|
||||||
|
, " Mise à jour du fichier du bitstream", $@.mem.log)
|
||||||
|
|
||||||
|
check_usb:
|
||||||
|
@lsusb | grep -q FT2232C || \
|
||||||
|
( echo "❌ Carte Digilent non détectée (FT2232C manquante)" && \
|
||||||
|
echo " → Vérifiez le câble USB, la carte ou changez de port." && \
|
||||||
|
exit 1 )
|
||||||
|
check_djtgcfg:
|
||||||
|
@command -v djtgcfg >/dev/null 2>&1 || \
|
||||||
|
( echo "❌ Outil djtgcfg introuvable." && \
|
||||||
|
echo " → Changez de machine ou rebootez !" && \
|
||||||
|
exit 1 )
|
||||||
|
check_driver: check_djtgcfg
|
||||||
|
@djtgcfg enum | grep -q "Digilent Zybo" || \
|
||||||
|
( echo "❌ Aucun périphérique détecté par djtgcfg." && \
|
||||||
|
echo " → Essayez un autre port USB ou vérifiez le câble." && \
|
||||||
|
exit 1 )
|
||||||
|
|
||||||
|
check_hw: check_usb check_driver
|
||||||
|
|
||||||
|
fpga: download.bit $(COMMON_DIR)/programFPGA.vivado.tcl check_hw ##! Génére le fichier de configuration du FPGA et le programme
|
||||||
|
@$(call check_vars,XILINX_VIVADO)
|
||||||
|
$(call q, \
|
||||||
|
$(XILINX_PREFIX)vivado -nolog -nojournal -mode batch -source $(COMMON_DIR)/programFPGA.vivado.tcl -nolog -nojournal \
|
||||||
|
-tclargs $< \
|
||||||
|
, " Programmation",prog.log )
|
||||||
|
|
||||||
|
##@ Nettoyage
|
||||||
|
clean: ##! Fais le nettoyage
|
||||||
|
$(call q, rm -rf $(TOCLEAN),)
|
||||||
15
bench/chenillard_minimaliste.s
Normal file
15
bench/chenillard_minimaliste.s
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
.text
|
||||||
|
ori x1, x0, 21
|
||||||
|
ori x2, x0, 1
|
||||||
|
addi x3, x3, 1 #Itérateur. Ne fonctionne que sur carte car x3 ne sera pas initialisé à 0 en simu.
|
||||||
|
srl x4, x3, x1 # Compteur tous les 2 million d'itérations environ (fréquence à l'Hertz)
|
||||||
|
and x4, x4, x2
|
||||||
|
xor x5, x4, x6 # détection de front (x6 étant la valeur précédente de x4)
|
||||||
|
or x6, x0, x4 # Sauvegarde de la valeur précédente
|
||||||
|
# Le motif à afficher est dans x7.
|
||||||
|
# On le décale de x5 (0 ou 1) position vers la gauche
|
||||||
|
sll x7, x7, x5
|
||||||
|
ori x7, x7, 1
|
||||||
|
or x31, x0, x7
|
||||||
|
# Via l'instruction de garde que rajoute l'assembleur, on réinitialise PC.
|
||||||
|
# Donc on reboucle sur la première instruction.
|
||||||
23
bench/chenillard_rotation.s
Normal file
23
bench/chenillard_rotation.s
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
.text
|
||||||
|
# Le motif du chenillard (2 bits consécutifs) est mis dans x1
|
||||||
|
# Le chenillard est sur 4 LED
|
||||||
|
ori x1, x0, 0x3
|
||||||
|
|
||||||
|
boucle_infinie:
|
||||||
|
# Durée d'attente dépend de la fréquence
|
||||||
|
li x2, 10000000
|
||||||
|
|
||||||
|
boucle_attente:
|
||||||
|
addi x2, x2, -1
|
||||||
|
bne x2, x0, boucle_attente
|
||||||
|
# On affiche le motif
|
||||||
|
ori x31, x1, 0
|
||||||
|
# On décale le motif
|
||||||
|
sll x1, x1, 1
|
||||||
|
# On cherche un dépassement en dehors des 4 LED
|
||||||
|
andi x2, x1, 0x10
|
||||||
|
beq x2, x0, boucle_infinie
|
||||||
|
# En cas de changement, on ramène la LED qui sort sur la première LED
|
||||||
|
andi x1, x1, 0xf
|
||||||
|
ori x1, x1, 0x1
|
||||||
|
j boucle_infinie
|
||||||
4
bench/compteur.s
Normal file
4
bench/compteur.s
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# expected: 00000001,00000002,00000003,00000004,00000005,00000006,00000007,00000008
|
||||||
|
.text
|
||||||
|
ori x1, x0, 1
|
||||||
|
add x31, x31, x1
|
||||||
16
bench/crt.S
Normal file
16
bench/crt.S
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
.section .text,"ax",@progbits
|
||||||
|
.equ STACK_SIZE, 1024
|
||||||
|
.globl _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
# réservation d'une (petite) zone de mémoire pour la pile
|
||||||
|
la sp, stack + STACK_SIZE
|
||||||
|
# appel de main, sans espoir de retour !
|
||||||
|
jal main
|
||||||
|
1: j 1b
|
||||||
|
|
||||||
|
.bss
|
||||||
|
.align 4
|
||||||
|
.global stack
|
||||||
|
stack:
|
||||||
|
.skip STACK_SIZE
|
||||||
38
bench/fmnsoc.h
Normal file
38
bench/fmnsoc.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#ifndef __FMNSOC_H__
|
||||||
|
#define __FMNSOC_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// Leds et boutons
|
||||||
|
// Même adresse, leds en écriture, boutons en lecture
|
||||||
|
#define LED_LEDS 0x30000000
|
||||||
|
#define LED_PUSHBUTTONS 0x30000000
|
||||||
|
|
||||||
|
// Vga sans configuration possible
|
||||||
|
#define FRAMEBUFFER 0x80000000
|
||||||
|
|
||||||
|
// CLint pour le timer
|
||||||
|
#define CLINT_MSIP 0x02000000
|
||||||
|
#define CLINT_TIMER_CMP 0x02004000
|
||||||
|
#define CLINT_TIMER_CMP_HI 0x02004004
|
||||||
|
#define CLINT_TIMER_CMP_LO 0x02004000
|
||||||
|
#define CLINT_TIMER 0x0200bff8
|
||||||
|
#define CLINT_TIMER_HI 0x0200bffc
|
||||||
|
#define CLINT_TIMER_LOW 0x0200bff8
|
||||||
|
|
||||||
|
// Vga 320x200, 8 bits par pixels
|
||||||
|
#define DISPLAY_WIDTH 320
|
||||||
|
#define DISPLAY_HEIGHT 240
|
||||||
|
#define DISPLAY_SIZE (DISPLAY_WIDTH * DISPLAY_HEIGHT)
|
||||||
|
#define DISPLAY_SCALE 1
|
||||||
|
|
||||||
|
#define TIMER_FREQ 10000000 // 10MHz
|
||||||
|
#define TIMER_RATIO 200
|
||||||
|
|
||||||
|
void timer_set(uint32_t period, uint32_t start_value);
|
||||||
|
void timer_wait(void);
|
||||||
|
void timer_set_and_wait(uint32_t period, uint32_t time);
|
||||||
|
void led_set(uint32_t value);
|
||||||
|
uint32_t push_button_get();
|
||||||
|
|
||||||
|
#endif // __FMNSOC_H__
|
||||||
438
bench/invaders.c
Normal file
438
bench/invaders.c
Normal file
@@ -0,0 +1,438 @@
|
|||||||
|
#include "fmnsoc.h"
|
||||||
|
#include "invaders.h"
|
||||||
|
#define N_OBJECTS 7
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Definition of bitmap for each line in the 8x8 pattern
|
||||||
|
*
|
||||||
|
* Since we have a word addressable bitmap, we need to reorder the patterns
|
||||||
|
* so that they display correctly.
|
||||||
|
* We do that in the definitions to avoid run time penalties.
|
||||||
|
*/
|
||||||
|
#define R(x) (((x & 0xf0) >> 4) | ((x & 0x0f) << 4))
|
||||||
|
static uint8_t sprite_sship[8] = {
|
||||||
|
R(0b00000000),
|
||||||
|
R(0b00111100),
|
||||||
|
R(0b01111110),
|
||||||
|
R(0b11111111),
|
||||||
|
R(0b11111111),
|
||||||
|
R(0b11100111),
|
||||||
|
R(0b11000011),
|
||||||
|
R(0b11000011),
|
||||||
|
};
|
||||||
|
static uint8_t sprite_laser[8] = {
|
||||||
|
R(0b00011000),
|
||||||
|
R(0b00011000),
|
||||||
|
R(0b00011000),
|
||||||
|
R(0b00011000),
|
||||||
|
R(0b00011000),
|
||||||
|
R(0b00011000),
|
||||||
|
R(0b00011000),
|
||||||
|
R(0b00011000),
|
||||||
|
};
|
||||||
|
static uint8_t sprite_alien1[8] = {
|
||||||
|
R(0b11000011),
|
||||||
|
R(0b00111100),
|
||||||
|
R(0b01011010),
|
||||||
|
R(0b11111111),
|
||||||
|
R(0b11111111),
|
||||||
|
R(0b10000001),
|
||||||
|
R(0b01000010),
|
||||||
|
R(0b00100100),
|
||||||
|
};
|
||||||
|
static uint8_t sprite_alien2[8] = {
|
||||||
|
R(0b11000011),
|
||||||
|
R(0b00111100),
|
||||||
|
R(0b01011010),
|
||||||
|
R(0b11111111),
|
||||||
|
R(0b11111111),
|
||||||
|
R(0b10100101),
|
||||||
|
R(0b10100101),
|
||||||
|
R(0b01011010),
|
||||||
|
};
|
||||||
|
static uint8_t sprite_alien3[8] = {
|
||||||
|
R(0b01000010),
|
||||||
|
R(0b00100100),
|
||||||
|
R(0b00111100),
|
||||||
|
R(0b01011010),
|
||||||
|
R(0b11111111),
|
||||||
|
R(0b10111101),
|
||||||
|
R(0b10000001),
|
||||||
|
R(0b01000010),
|
||||||
|
};
|
||||||
|
static uint8_t sprite_alien4[8] = {
|
||||||
|
R(0b11000011),
|
||||||
|
R(0b00100100),
|
||||||
|
R(0b10111101),
|
||||||
|
R(0b11100111),
|
||||||
|
R(0b00111100),
|
||||||
|
R(0b00100100),
|
||||||
|
R(0b01011010),
|
||||||
|
R(0b10011001),
|
||||||
|
};
|
||||||
|
static uint8_t sprite_alien5[8] = {
|
||||||
|
R(0b01100110),
|
||||||
|
R(0b01111110),
|
||||||
|
R(0b11011011),
|
||||||
|
R(0b01111110),
|
||||||
|
R(0b10111101),
|
||||||
|
R(0b10100101),
|
||||||
|
R(0b10000001),
|
||||||
|
R(0b01100110),
|
||||||
|
};
|
||||||
|
// bleu, vert, rouge pour tenir dans l'uint8_t de color_t comme il faut
|
||||||
|
static const color_t black = {0, 0, 0};
|
||||||
|
static const color_t white = {7, 7, 3};
|
||||||
|
static const color_t red = {0, 0, 3};
|
||||||
|
static const color_t green = {0, 7, 0};
|
||||||
|
static const color_t blue = {7, 0, 0};
|
||||||
|
static const color_t magenta = {7, 0, 3};
|
||||||
|
static const color_t cyan = {7, 7, 1};
|
||||||
|
static const color_t yellow = {0, 7, 3};
|
||||||
|
|
||||||
|
/* sprite objects */
|
||||||
|
object_t object[N_OBJECTS] = {
|
||||||
|
/* blue spaceship */
|
||||||
|
{1, 3, 1, 18, 23, 0, 0, sprite_sship, blue, {[0 ... 7]={0}}, 0, 0},
|
||||||
|
/* white laser */
|
||||||
|
{0, 1, 1, 18, 0, 0, 0, sprite_laser, white, {[0 ... 7]={0}}, 0, 0},
|
||||||
|
/* green alien */
|
||||||
|
{1, 4, 1, 10, 0, -1, 0, sprite_alien1, green, {[0 ... 7]={0}}, 0, 0},
|
||||||
|
/* red alien */
|
||||||
|
{1, 4, 1, 18, 0, -1, 0, sprite_alien2, red, {[0 ... 7]={0}}, 0, 0},
|
||||||
|
/* magenta alien */
|
||||||
|
{1, 4, 1, 26, 0, -1, 0, sprite_alien3, magenta, {[0 ... 7]={0}}, 0, 0},
|
||||||
|
/* yellow alien */
|
||||||
|
{1, 4, 1, 14, 2, -1, 0, sprite_alien4, yellow, {[0 ... 7]={0}}, 0, 0},
|
||||||
|
/* cyan alien */
|
||||||
|
{1, 4, 1, 22, 2, -1, 0, sprite_alien5, cyan, {[0 ... 7]={0}}, 0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Tableau d'états pour la mécanique du jeu */
|
||||||
|
state_t state[5] = {
|
||||||
|
{0, 1, 1},
|
||||||
|
{0, 1, 2},
|
||||||
|
{1, 0, 3},
|
||||||
|
{0, 1, 4},
|
||||||
|
{-1, 0, 1}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Deux pointeurs vers le frame buffer pour éviter les problèmes
|
||||||
|
* d'aliasing sur les types.
|
||||||
|
*/
|
||||||
|
static volatile color_t *img = (volatile color_t *)FRAMEBUFFER;
|
||||||
|
static volatile uint32_t *framebuffer = (volatile uint32_t *)FRAMEBUFFER;
|
||||||
|
static volatile uint32_t *led = (volatile uint32_t *)LED_LEDS;
|
||||||
|
static volatile uint32_t *push = (volatile uint32_t *)LED_PUSHBUTTONS;
|
||||||
|
static volatile uint64_t *timer = (volatile uint64_t *)CLINT_TIMER;
|
||||||
|
static volatile uint64_t *timer_cmp = (volatile uint64_t *)CLINT_TIMER_CMP;
|
||||||
|
static volatile uint32_t *timer_hi = (volatile uint32_t *)CLINT_TIMER_HI;
|
||||||
|
static volatile uint32_t *timer_lo = (volatile uint32_t *)CLINT_TIMER_LOW;
|
||||||
|
static volatile uint32_t *timer_cmp_hi = (volatile uint32_t *)CLINT_TIMER_CMP_HI;
|
||||||
|
static volatile uint32_t *timer_cmp_lo = (volatile uint32_t *)CLINT_TIMER_CMP_LO;
|
||||||
|
|
||||||
|
uint32_t mult(uint64_t x, uint64_t y)
|
||||||
|
{
|
||||||
|
uint64_t res = 0;
|
||||||
|
while (y != 0) {
|
||||||
|
if (y % 2 == 1) {
|
||||||
|
res += x;
|
||||||
|
}
|
||||||
|
x <<= 1;
|
||||||
|
y >>= 1;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void timer_set(uint32_t period, uint32_t time)
|
||||||
|
{
|
||||||
|
uint64_t now = *timer;
|
||||||
|
*timer_cmp = now + mult(((uint64_t)period >> 8), time);
|
||||||
|
}
|
||||||
|
|
||||||
|
void timer_wait(void)
|
||||||
|
{
|
||||||
|
while (*timer <= *timer_cmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void timer_set_and_wait(uint32_t period, uint32_t time)
|
||||||
|
{
|
||||||
|
timer_set(period, time);
|
||||||
|
timer_wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
void led_set(uint32_t value)
|
||||||
|
{
|
||||||
|
*led = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t push_button_get()
|
||||||
|
{
|
||||||
|
return *push;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* main program
|
||||||
|
* ---------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
/* declaration of local variables */
|
||||||
|
uint32_t i;
|
||||||
|
uint32_t push_state, led_state, alien_state, edge_reached;
|
||||||
|
uint32_t n_aliens;
|
||||||
|
object_t *spaceship, *laser;
|
||||||
|
|
||||||
|
init:
|
||||||
|
/* initialization stage */
|
||||||
|
push_state = 0; /* no button pressed at beginning */
|
||||||
|
led_state = 0; /* initial value displayed on leds */
|
||||||
|
alien_state = 0; /* state of alien in a line */
|
||||||
|
edge_reached = 0; /* no edge reached at beginning */
|
||||||
|
n_aliens = N_OBJECTS - 2; /* number of displayed aliens */
|
||||||
|
initialize();
|
||||||
|
laser = &object[1]; /* laser is the second declared object */
|
||||||
|
spaceship = &object[0]; /* spaceship is the first declared object */
|
||||||
|
spaceship->x = 20; /* set spaceship at the middle */
|
||||||
|
spaceship->y = 25; /* and bottom of the screen */
|
||||||
|
#define MAX_X 39
|
||||||
|
|
||||||
|
/* display stage */
|
||||||
|
while (1) {
|
||||||
|
edge_reached = 0;
|
||||||
|
|
||||||
|
/* decrease deadline of alive objects */
|
||||||
|
for (i = 0; i < N_OBJECTS; i++) {
|
||||||
|
if (object[i].alive)
|
||||||
|
object[i].deadline--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* display all alive objects */
|
||||||
|
for (i = 0; i < N_OBJECTS; i++) {
|
||||||
|
if (object[i].alive)
|
||||||
|
display_sprite(&object[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* determine new positions of all alive objects */
|
||||||
|
for (i = 0; i < N_OBJECTS; i++) {
|
||||||
|
/* update object state when deadline is reached */
|
||||||
|
if (object[i].alive && object[i].deadline == 0) {
|
||||||
|
/* reinitialize the object deadline to period */
|
||||||
|
object[i].deadline = object[i].period;
|
||||||
|
/* determine new position and manage screen edges */
|
||||||
|
object[i].x += object[i].dx;
|
||||||
|
if (object[i].x < 0)
|
||||||
|
object[i].x = 0;
|
||||||
|
if (object[i].x > MAX_X)
|
||||||
|
object[i].x = MAX_X;
|
||||||
|
object[i].y += object[i].dy;
|
||||||
|
/* test if an edge of the screen was reached by an alien */
|
||||||
|
if (i >= 2 && (object[i].x == 0 || object[i].x == MAX_X))
|
||||||
|
edge_reached = 1;
|
||||||
|
/* store background of the next position */
|
||||||
|
if (i > 1 && object[i].y >= spaceship->y) {
|
||||||
|
clear_screen(blue); /* blue screen */
|
||||||
|
timer_set_and_wait(TIMER_FREQ, 1000);
|
||||||
|
initialize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* test if alien is hit by an alive laser */
|
||||||
|
if (laser->alive) {
|
||||||
|
for (i = 2; i < N_OBJECTS; i++) {
|
||||||
|
if (object[i].alive && laser->x == object[i].x && laser->y == object[i].y) {
|
||||||
|
n_aliens--;
|
||||||
|
object[i].alive = 0;
|
||||||
|
laser->alive = 0;
|
||||||
|
if (n_aliens == 0) {
|
||||||
|
/* no more aliens */
|
||||||
|
spaceship->alive = 0;
|
||||||
|
clear_screen(yellow); /* yellow screen */
|
||||||
|
timer_set_and_wait(TIMER_FREQ, 1000);
|
||||||
|
clear_screen(red); /* red screen */
|
||||||
|
} else {
|
||||||
|
display_sprite(&object[i]);
|
||||||
|
display_sprite(laser);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* when an alien reaches a screen edge, the group of aliens is moved */
|
||||||
|
if (edge_reached) {
|
||||||
|
for (i = 2; i < N_OBJECTS; i++) {
|
||||||
|
object[i].dx = state[alien_state].dx;
|
||||||
|
object[i].dy = state[alien_state].dy;
|
||||||
|
}
|
||||||
|
alien_state = state[alien_state].next_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* laser disappears when it reaches the screen top */
|
||||||
|
if (laser->alive && laser->y == 0) {
|
||||||
|
laser->alive = 0;
|
||||||
|
display_sprite(laser);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* manage push buttons */
|
||||||
|
push_state = push_button_get();
|
||||||
|
// if we won, press fire to restart
|
||||||
|
if ((n_aliens == 0)
|
||||||
|
&& (push_state & 0x4)) {
|
||||||
|
goto init;
|
||||||
|
}
|
||||||
|
if ((spaceship->deadline == 1)
|
||||||
|
|| (n_aliens == 0)) {
|
||||||
|
spaceship->dx = 0;
|
||||||
|
if (push_state & 0x1)
|
||||||
|
/* to the right */
|
||||||
|
spaceship->dx = 1;
|
||||||
|
if (push_state & 0x2)
|
||||||
|
/* to the left */
|
||||||
|
spaceship->dx = -1;
|
||||||
|
if (push_state & 0x4) {
|
||||||
|
/* fire a laser */
|
||||||
|
if (!laser->alive) {
|
||||||
|
laser->alive = 1;
|
||||||
|
laser->dx = 0;
|
||||||
|
laser->dy = -1;
|
||||||
|
laser->x = spaceship->x;
|
||||||
|
laser->y = spaceship->y - 1;
|
||||||
|
laser->deadline = laser->period;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* manage leds' state */
|
||||||
|
led_set(led_state);
|
||||||
|
led_state++;
|
||||||
|
timer_set_and_wait(TIMER_FREQ, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* definition of functions
|
||||||
|
* ---------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* function to read a pixel from a (x,y) position of video framebuffer */
|
||||||
|
color_t read_pixel(uint32_t x, uint32_t y)
|
||||||
|
{
|
||||||
|
uint32_t real_y = y * DISPLAY_WIDTH * DISPLAY_SCALE;
|
||||||
|
uint32_t real_x = x * DISPLAY_SCALE;
|
||||||
|
return img[real_y + real_x];
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_pixel_scaling(color_t pixel, uint32_t x, uint32_t y)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < DISPLAY_SCALE; ++i) {
|
||||||
|
for (int j = 0; j < DISPLAY_SCALE; ++j) {
|
||||||
|
uint32_t real_y = y * DISPLAY_SCALE + i;
|
||||||
|
uint32_t real_x = x * DISPLAY_SCALE + j;
|
||||||
|
|
||||||
|
img[real_y * DISPLAY_WIDTH + real_x] = pixel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void memsetw(volatile uint32_t* dest, uint32_t c, uint32_t n)
|
||||||
|
{
|
||||||
|
volatile uint32_t *p = dest;
|
||||||
|
while (n-- > 0) {
|
||||||
|
*(volatile uint32_t *)dest++ = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear_screen(color_t color)
|
||||||
|
{
|
||||||
|
pixels_t p;
|
||||||
|
p.pixel[0] = color;
|
||||||
|
p.pixel[1] = color;
|
||||||
|
p.pixel[2] = color;
|
||||||
|
p.pixel[3] = color;
|
||||||
|
memsetw(framebuffer, p.pixels, DISPLAY_SIZE / 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* function to initialize all objects */
|
||||||
|
void initialize()
|
||||||
|
{
|
||||||
|
uint32_t i, dx, dy;
|
||||||
|
clear_screen(black); /* black screen */
|
||||||
|
for (i = 0; i < N_OBJECTS; i++) {
|
||||||
|
if (i == 1) {
|
||||||
|
/* laser */
|
||||||
|
object[i].alive = 0;
|
||||||
|
object[i].period = 1;
|
||||||
|
} else {
|
||||||
|
/* spaceship or aliens */
|
||||||
|
object[i].alive = 1;
|
||||||
|
if (i == 0)
|
||||||
|
/* spaceship */
|
||||||
|
object[i].period = 3;
|
||||||
|
else
|
||||||
|
/* aliens */
|
||||||
|
object[i].period = 4;
|
||||||
|
}
|
||||||
|
object[i].deadline = 1;
|
||||||
|
if (i > 1) {
|
||||||
|
/* aliens */
|
||||||
|
if (i > 4) {
|
||||||
|
/* alien4 or alien5 */
|
||||||
|
object[i].y = 3; /* 3rd line */
|
||||||
|
object[i].x = 6 + (i - 4) * 8;
|
||||||
|
} else {
|
||||||
|
/* alien1, alien2 or alien3 */
|
||||||
|
object[i].y = 1; /* 1st line */
|
||||||
|
object[i].x = 10 + (i - 2) * 8;
|
||||||
|
}
|
||||||
|
object[i].dx = -1;
|
||||||
|
object[i].dy = 0;
|
||||||
|
}
|
||||||
|
object[i].ax = -1;
|
||||||
|
object[i].ay = -1;
|
||||||
|
|
||||||
|
/* initialization of object background considering the last one */
|
||||||
|
for (dx = 0; dx < 8; dx++)
|
||||||
|
for (dy = 0; dy < 8; dy++)
|
||||||
|
object[i].bg[dx][dy] = black;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* function to display the 8 pixels of a pattern line */
|
||||||
|
void display_pattern_line(uint8_t m, uint32_t x, uint32_t y, color_t color)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
color_t new_color = (m & 1) == 1 ? color : black;
|
||||||
|
m >>= 1;
|
||||||
|
write_pixel_scaling(new_color, x + i, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* function to display an 8x8 object considering the last background */
|
||||||
|
void display_pattern(uint8_t pattern[8], uint32_t x, uint32_t y, color_t color)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
display_pattern_line(pattern[i], x, y + i, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* function to display an 8x8 object (spaceship, laser or alien) */
|
||||||
|
void display_sprite(object_t *object)
|
||||||
|
{
|
||||||
|
if ((object->ax > -1 && object->ay > -1) && (object->x != object->ax || object->y != object->ay || !object->alive)) {
|
||||||
|
for (uint32_t dx = 0; dx < 8; dx++) {
|
||||||
|
for (uint32_t dy = 0; dy < 8; dy++) {
|
||||||
|
write_pixel_scaling(object->bg[dx][dy], ((object->ax) << 3) + dx, ((object->ay) << 3) + dy);
|
||||||
|
if (!object->alive)
|
||||||
|
object->bg[dx][dy] = black;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object->ax = object->x;
|
||||||
|
object->ay = object->y;
|
||||||
|
|
||||||
|
if (object->alive)
|
||||||
|
display_pattern(object->pattern, (object->x) << 3, (object->y) << 3, object->color);
|
||||||
|
}
|
||||||
49
bench/invaders.h
Normal file
49
bench/invaders.h
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
#ifndef __SPRITE_H__
|
||||||
|
#define __SPRITE_H__
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define OBJECT_SIZE 8
|
||||||
|
|
||||||
|
struct color_t {
|
||||||
|
uint8_t b:3;
|
||||||
|
uint8_t g:3;
|
||||||
|
uint8_t r:2;
|
||||||
|
} __attribute__((packed));
|
||||||
|
typedef struct color_t color_t;
|
||||||
|
|
||||||
|
typedef union {
|
||||||
|
color_t pixel[4];
|
||||||
|
uint32_t pixels;
|
||||||
|
} pixels_t;
|
||||||
|
|
||||||
|
|
||||||
|
/* Objet pour représenter les aliens, le vaisseau et le laser */
|
||||||
|
typedef struct object_t {
|
||||||
|
uint32_t alive;
|
||||||
|
uint32_t period;
|
||||||
|
uint32_t deadline;
|
||||||
|
int x, y;
|
||||||
|
int dx, dy;
|
||||||
|
uint8_t *pattern;
|
||||||
|
color_t color;
|
||||||
|
color_t bg[OBJECT_SIZE][OBJECT_SIZE];
|
||||||
|
int ax, ay;
|
||||||
|
} object_t;
|
||||||
|
|
||||||
|
/* État pour implanter la mécanique du jeu sous forme de machine à états */
|
||||||
|
typedef struct state_t {
|
||||||
|
int dx;
|
||||||
|
int dy;
|
||||||
|
int next_state;
|
||||||
|
} state_t;
|
||||||
|
|
||||||
|
/* Prototype des fonctions définies dans invaders.c */
|
||||||
|
color_t read_pixel(uint32_t x, uint32_t y);
|
||||||
|
void write_pixel_scaling(color_t pixel, uint32_t x, uint32_t y);
|
||||||
|
void clear_screen(color_t color);
|
||||||
|
void initialize(void);
|
||||||
|
void display_pattern_line(uint8_t m, uint32_t x, uint32_t y, color_t color);
|
||||||
|
void display_pattern(uint8_t pattern[OBJECT_SIZE], uint32_t x, uint32_t y, color_t color);
|
||||||
|
void display_sprite(object_t *object);
|
||||||
|
void display_timer(void);
|
||||||
|
#endif
|
||||||
47
bench/link.ld
Normal file
47
bench/link.ld
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
OUTPUT_ARCH( "riscv" )
|
||||||
|
|
||||||
|
ENTRY (_start)
|
||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
insnmem (rx) : ORIGIN = 0x0000, LENGTH = 8K
|
||||||
|
datamem (wai!x) : ORIGIN = 0x2000, LENGTH = 8K
|
||||||
|
}
|
||||||
|
|
||||||
|
PHDRS
|
||||||
|
{
|
||||||
|
text PT_LOAD;
|
||||||
|
data PT_LOAD;
|
||||||
|
bss PT_LOAD;
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
PROVIDE(_start = ORIGIN(insnmem));
|
||||||
|
.text : {
|
||||||
|
PROVIDE(_text_start = .);
|
||||||
|
*(.text._start) *(.text .text.*)
|
||||||
|
PROVIDE(_text_end = .);
|
||||||
|
} >insnmem AT>insnmem :text
|
||||||
|
|
||||||
|
. = ORIGIN(datamem);
|
||||||
|
.rodata ALIGN(4) : {
|
||||||
|
PROVIDE(_rodata_start = .);
|
||||||
|
*(.rodata .rodata.*)
|
||||||
|
PROVIDE(_rodata_end = .);
|
||||||
|
} >datamem AT>datamem :data
|
||||||
|
|
||||||
|
.data ALIGN(4) : {
|
||||||
|
PROVIDE(_data_start = .);
|
||||||
|
*(.sdata .sdata.*) *(.data .data.*)
|
||||||
|
PROVIDE(_data_end = .);
|
||||||
|
} >datamem AT>datamem :data
|
||||||
|
|
||||||
|
.bss ALIGN(4) : {
|
||||||
|
PROVIDE(_bss_start = .);
|
||||||
|
*(.sbss .sbss.*) *(.bss .bss.*)
|
||||||
|
PROVIDE(_bss_end = .);
|
||||||
|
} >datamem AT>datamem :bss
|
||||||
|
|
||||||
|
PROVIDE(_memory_start = ORIGIN(datamem));
|
||||||
|
PROVIDE(_memory_end = ORIGIN(datamem) + LENGTH(datamem));
|
||||||
|
}
|
||||||
7
bench/lui.s
Normal file
7
bench/lui.s
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# expected: 00000000,FFFFF000,12345000
|
||||||
|
.text
|
||||||
|
|
||||||
|
lui x31, 0 #Test chargement d'une valeur nulle
|
||||||
|
lui x31, 0xfffff #Test chargement d'une valeur maximal sur 20 bits
|
||||||
|
lui x31, 0x12345 #Test chargement d'une valeur quelconque
|
||||||
|
|
||||||
41
build.sc
Normal file
41
build.sc
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
// import Mill dependency
|
||||||
|
import mill._
|
||||||
|
import mill.define.Sources
|
||||||
|
import mill.modules.Util
|
||||||
|
import mill.scalalib.TestModule.ScalaTest
|
||||||
|
import scalalib._
|
||||||
|
// support BSP
|
||||||
|
import mill.bsp._
|
||||||
|
|
||||||
|
// Note: This project requires .mill-jvm-opts file containing:
|
||||||
|
// -Dchisel.project.root=${PWD}
|
||||||
|
// This is needed because Chisel needs to know the project root directory
|
||||||
|
// to properly generate and handle test directories and output files.
|
||||||
|
// See: https://github.com/com-lihaoyi/mill/issues/3840
|
||||||
|
|
||||||
|
object `TPchisel` extends SbtModule { m =>
|
||||||
|
override def millSourcePath = super.millSourcePath / os.up
|
||||||
|
override def scalaVersion = "2.13.16"
|
||||||
|
override def scalacOptions = Seq(
|
||||||
|
"-language:reflectiveCalls",
|
||||||
|
"-deprecation",
|
||||||
|
"-feature",
|
||||||
|
"-Xcheckinit",
|
||||||
|
"-Ymacro-annotations",
|
||||||
|
"-unchecked",
|
||||||
|
"-Xfatal-warnings",
|
||||||
|
"-Ywarn-dead-code",
|
||||||
|
"-Ywarn-unused"
|
||||||
|
)
|
||||||
|
override def ivyDeps = Agg(
|
||||||
|
ivy"org.chipsalliance::chisel:7.2.0",
|
||||||
|
)
|
||||||
|
override def scalacPluginIvyDeps = Agg(
|
||||||
|
ivy"org.chipsalliance:::chisel-plugin:7.2.0",
|
||||||
|
)
|
||||||
|
object test extends SbtTests with TestModule.ScalaTest {
|
||||||
|
override def ivyDeps = m.ivyDeps() ++ Agg(
|
||||||
|
ivy"org.scalatest::scalatest::3.2.19"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
147
common/ZzTop.xdc
Normal file
147
common/ZzTop.xdc
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
## This file is a general .xdc for the ZYBO Rev B board
|
||||||
|
## To use it in a project:
|
||||||
|
## - uncomment the lines corresponding to used pins
|
||||||
|
## - rename the used signals according to the project
|
||||||
|
|
||||||
|
|
||||||
|
# Clock signal
|
||||||
|
set_property -dict { PACKAGE_PIN L16 IOSTANDARD LVCMOS33 } [get_ports { clock }]; #IO_L11P_T1_SRCC_35 Sch=sysclk
|
||||||
|
|
||||||
|
create_clock -add -name sys_clk_pin -period 8.00 -waveform {0 4} [get_ports { clock }];
|
||||||
|
|
||||||
|
# Switches
|
||||||
|
set_property -dict { PACKAGE_PIN G15 IOSTANDARD LVCMOS33 } [get_ports { io_switch[0] }]; #IO_L19N_T3_VREF_35 Sch=SW0
|
||||||
|
set_property -dict { PACKAGE_PIN P15 IOSTANDARD LVCMOS33 } [get_ports { io_switch[1] }]; #IO_L24P_T3_34 Sch=SW1
|
||||||
|
set_property -dict { PACKAGE_PIN W13 IOSTANDARD LVCMOS33 } [get_ports { io_switch[2] }]; #IO_L4N_T0_34 Sch=SW2
|
||||||
|
set_property -dict { PACKAGE_PIN T16 IOSTANDARD LVCMOS33 } [get_ports { io_switch[3] }]; #IO_L9P_T1_DQS_34 Sch=SW3
|
||||||
|
|
||||||
|
|
||||||
|
# Buttons
|
||||||
|
set_property -dict { PACKAGE_PIN R18 IOSTANDARD LVCMOS33 } [get_ports { io_push[0] }]; #IO_L20N_T3_34 Sch=BTN0
|
||||||
|
set_property -dict { PACKAGE_PIN P16 IOSTANDARD LVCMOS33 } [get_ports { io_push[1] }]; #IO_L24N_T3_34 Sch=BTN1
|
||||||
|
set_property -dict { PACKAGE_PIN V16 IOSTANDARD LVCMOS33 } [get_ports { io_push[2] }]; #IO_L18P_T2_34 Sch=BTN2
|
||||||
|
set_property -dict { PACKAGE_PIN Y16 IOSTANDARD LVCMOS33 } [get_ports { reset }]; #IO_L7P_T1_34 Sch=BTN3
|
||||||
|
|
||||||
|
|
||||||
|
# LEDs
|
||||||
|
set_property -dict { PACKAGE_PIN M14 IOSTANDARD LVCMOS33 } [get_ports { io_led[0] }]; #IO_L23P_T3_35 Sch=LED0
|
||||||
|
set_property -dict { PACKAGE_PIN M15 IOSTANDARD LVCMOS33 } [get_ports { io_led[1] }]; #IO_L23N_T3_35 Sch=LED1
|
||||||
|
set_property -dict { PACKAGE_PIN G14 IOSTANDARD LVCMOS33 } [get_ports { io_led[2] }]; #IO_0_35 Sch=LED2
|
||||||
|
set_property -dict { PACKAGE_PIN D18 IOSTANDARD LVCMOS33 } [get_ports { io_led[3] }]; #IO_L3N_T0_DQS_AD1N_35 Sch=LED3
|
||||||
|
|
||||||
|
|
||||||
|
##I2S Audio Codec
|
||||||
|
#set_property -dict { PACKAGE_PIN K18 IOSTANDARD LVCMOS33 } [get_ports { ac_bclk }]; #IO_L12N_T1_MRCC_35 Sch=AC_BCLK
|
||||||
|
#set_property -dict { PACKAGE_PIN T19 IOSTANDARD LVCMOS33 } [get_ports { ac_mclk }]; #IO_25_34 Sch=AC_MCLK
|
||||||
|
#set_property -dict { PACKAGE_PIN P18 IOSTANDARD LVCMOS33 } [get_ports { ac_muten }]; #IO_L23N_T3_34 Sch=AC_MUTEN
|
||||||
|
#set_property -dict { PACKAGE_PIN M17 IOSTANDARD LVCMOS33 } [get_ports { ac_pbdat }]; #IO_L8P_T1_AD10P_35 Sch=AC_PBDAT
|
||||||
|
#set_property -dict { PACKAGE_PIN L17 IOSTANDARD LVCMOS33 } [get_ports { ac_pblrc }]; #IO_L11N_T1_SRCC_35 Sch=AC_PBLRC
|
||||||
|
#set_property -dict { PACKAGE_PIN K17 IOSTANDARD LVCMOS33 } [get_ports { ac_recdat }]; #IO_L12P_T1_MRCC_35 Sch=AC_RECDAT
|
||||||
|
#set_property -dict { PACKAGE_PIN M18 IOSTANDARD LVCMOS33 } [get_ports { ac_reclrc }]; #IO_L8N_T1_AD10N_35 Sch=AC_RECLRC
|
||||||
|
|
||||||
|
|
||||||
|
##Audio Codec/external EEPROM IIC bus
|
||||||
|
#set_property -dict { PACKAGE_PIN N18 IOSTANDARD LVCMOS33 } [get_ports { ac_scl }]; #IO_L13P_T2_MRCC_34 Sch=AC_SCL
|
||||||
|
#set_property -dict { PACKAGE_PIN N17 IOSTANDARD LVCMOS33 } [get_ports { ac_sda }]; #IO_L23P_T3_34 Sch=AC_SDA
|
||||||
|
|
||||||
|
|
||||||
|
##Additional Ethernet signals
|
||||||
|
#set_property -dict { PACKAGE_PIN F16 IOSTANDARD LVCMOS33 } [get_ports { eth_int_b }]; #IO_L6P_T0_35 Sch=ETH_INT_B
|
||||||
|
#set_property -dict { PACKAGE_PIN E17 IOSTANDARD LVCMOS33 } [get_ports { eth_rst_b }]; #IO_L3P_T0_DQS_AD1P_35 Sch=ETH_RST_B
|
||||||
|
|
||||||
|
|
||||||
|
##HDMI Signals
|
||||||
|
#set_property -dict { PACKAGE_PIN H17 IOSTANDARD TMDS_33 } [get_ports { hdmi_clk_n }]; #IO_L13N_T2_MRCC_35 Sch=HDMI_CLK_N
|
||||||
|
#set_property -dict { PACKAGE_PIN H16 IOSTANDARD TMDS_33 } [get_ports { hdmi_clk_p }]; #IO_L13P_T2_MRCC_35 Sch=HDMI_CLK_P
|
||||||
|
#set_property -dict { PACKAGE_PIN D20 IOSTANDARD TMDS_33 } [get_ports { hdmi_d_n[0] }]; #IO_L4N_T0_35 Sch=HDMI_D0_N
|
||||||
|
#set_property -dict { PACKAGE_PIN D19 IOSTANDARD TMDS_33 } [get_ports { hdmi_d_p[0] }]; #IO_L4P_T0_35 Sch=HDMI_D0_P
|
||||||
|
#set_property -dict { PACKAGE_PIN B20 IOSTANDARD TMDS_33 } [get_ports { hdmi_d_n[1] }]; #IO_L1N_T0_AD0N_35 Sch=HDMI_D1_N
|
||||||
|
#set_property -dict { PACKAGE_PIN C20 IOSTANDARD TMDS_33 } [get_ports { hdmi_d_p[1] }]; #IO_L1P_T0_AD0P_35 Sch=HDMI_D1_P
|
||||||
|
#set_property -dict { PACKAGE_PIN A20 IOSTANDARD TMDS_33 } [get_ports { hdmi_d_n[2] }]; #IO_L2N_T0_AD8N_35 Sch=HDMI_D2_N
|
||||||
|
#set_property -dict { PACKAGE_PIN B19 IOSTANDARD TMDS_33 } [get_ports { hdmi_d_p[2] }]; #IO_L2P_T0_AD8P_35 Sch=HDMI_D2_P
|
||||||
|
#set_property -dict { PACKAGE_PIN E19 IOSTANDARD LVCMOS33 } [get_ports { hdmi_cec }]; #IO_L5N_T0_AD9N_35 Sch=HDMI_CEC
|
||||||
|
#set_property -dict { PACKAGE_PIN E18 IOSTANDARD LVCMOS33 } [get_ports { hdmi_hpd }]; #IO_L5P_T0_AD9P_35 Sch=HDMI_HPD
|
||||||
|
#set_property -dict { PACKAGE_PIN F17 IOSTANDARD LVCMOS33 } [get_ports { hdmi_out_en }]; #IO_L6N_T0_VREF_35 Sch=HDMI_OUT_EN
|
||||||
|
#set_property -dict { PACKAGE_PIN G17 IOSTANDARD LVCMOS33 } [get_ports { hdmi_scl }]; #IO_L16P_T2_35 Sch=HDMI_SCL
|
||||||
|
#set_property -dict { PACKAGE_PIN G18 IOSTANDARD LVCMOS33 } [get_ports { hdmi_sda }]; #IO_L16N_T2_35 Sch=HDMI_SDA
|
||||||
|
|
||||||
|
|
||||||
|
##Pmod Header JA (XADC)
|
||||||
|
#set_property -dict { PACKAGE_PIN N15 IOSTANDARD LVCMOS33 } [get_ports { ja_p[0] }]; #IO_L21P_T3_DQS_AD14P_35 Sch=JA1_R_p
|
||||||
|
#set_property -dict { PACKAGE_PIN L14 IOSTANDARD LVCMOS33 } [get_ports { ja_p[1] }]; #IO_L22P_T3_AD7P_35 Sch=JA2_R_P
|
||||||
|
#set_property -dict { PACKAGE_PIN K16 IOSTANDARD LVCMOS33 } [get_ports { ja_p[2] }]; #IO_L24P_T3_AD15P_35 Sch=JA3_R_P
|
||||||
|
#set_property -dict { PACKAGE_PIN K14 IOSTANDARD LVCMOS33 } [get_ports { ja_p[3] }]; #IO_L20P_T3_AD6P_35 Sch=JA4_R_P
|
||||||
|
#set_property -dict { PACKAGE_PIN N16 IOSTANDARD LVCMOS33 } [get_ports { ja_n[0] }]; #IO_L21N_T3_DQS_AD14N_35 Sch=JA1_R_N
|
||||||
|
#set_property -dict { PACKAGE_PIN L15 IOSTANDARD LVCMOS33 } [get_ports { ja_n[1] }]; #IO_L22N_T3_AD7N_35 Sch=JA2_R_N
|
||||||
|
#set_property -dict { PACKAGE_PIN J16 IOSTANDARD LVCMOS33 } [get_ports { ja_n[2] }]; #IO_L24N_T3_AD15N_35 Sch=JA3_R_N
|
||||||
|
#set_property -dict { PACKAGE_PIN J14 IOSTANDARD LVCMOS33 } [get_ports { ja_n[3] }]; #IO_L20N_T3_AD6N_35 Sch=JA4_R_N
|
||||||
|
|
||||||
|
|
||||||
|
##Pmod Header JB
|
||||||
|
#set_property -dict { PACKAGE_PIN T20 IOSTANDARD LVCMOS33 } [get_ports { jb_p[0] }]; #IO_L15P_T2_DQS_34 Sch=JB1_p
|
||||||
|
#set_property -dict { PACKAGE_PIN U20 IOSTANDARD LVCMOS33 } [get_ports { jb_n[0] }]; #IO_L15N_T2_DQS_34 Sch=JB1_N
|
||||||
|
#set_property -dict { PACKAGE_PIN V20 IOSTANDARD LVCMOS33 } [get_ports { jb_p[1] }]; #IO_L16P_T2_34 Sch=JB2_P
|
||||||
|
#set_property -dict { PACKAGE_PIN W20 IOSTANDARD LVCMOS33 } [get_ports { jb_n[1] }]; #IO_L16N_T2_34 Sch=JB2_N
|
||||||
|
#set_property -dict { PACKAGE_PIN Y18 IOSTANDARD LVCMOS33 } [get_ports { jb_p[2] }]; #IO_L17P_T2_34 Sch=JB3_P
|
||||||
|
#set_property -dict { PACKAGE_PIN Y19 IOSTANDARD LVCMOS33 } [get_ports { jb_n[2] }]; #IO_L17N_T2_34 Sch=JB3_N
|
||||||
|
#set_property -dict { PACKAGE_PIN W18 IOSTANDARD LVCMOS33 } [get_ports { jb_p[3] }]; #IO_L22P_T3_34 Sch=JB4_P
|
||||||
|
#set_property -dict { PACKAGE_PIN W19 IOSTANDARD LVCMOS33 } [get_ports { jb_n[3] }]; #IO_L22N_T3_34 Sch=JB4_N
|
||||||
|
|
||||||
|
|
||||||
|
##Pmod Header JC
|
||||||
|
#set_property -dict { PACKAGE_PIN V15 IOSTANDARD LVCMOS33 } [get_ports { jc_p[0] }]; #IO_L10P_T1_34 Sch=JC1_P
|
||||||
|
#set_property -dict { PACKAGE_PIN W15 IOSTANDARD LVCMOS33 } [get_ports { jc_n[0] }]; #IO_L10N_T1_34 Sch=JC1_N
|
||||||
|
#set_property -dict { PACKAGE_PIN T11 IOSTANDARD LVCMOS33 } [get_ports { jc_p[1] }]; #IO_L1P_T0_34 Sch=JC2_P
|
||||||
|
#set_property -dict { PACKAGE_PIN T10 IOSTANDARD LVCMOS33 } [get_ports { jc_n[1] }]; #IO_L1N_T0_34 Sch=JC2_N
|
||||||
|
#set_property -dict { PACKAGE_PIN W14 IOSTANDARD LVCMOS33 } [get_ports { jc_p[2] }]; #IO_L8P_T1_34 Sch=JC3_P
|
||||||
|
#set_property -dict { PACKAGE_PIN Y14 IOSTANDARD LVCMOS33 } [get_ports { jc_n[2] }]; #IO_L8N_T1_34 Sch=JC3_N
|
||||||
|
#set_property -dict { PACKAGE_PIN T12 IOSTANDARD LVCMOS33 } [get_ports { jc_p[3] }]; #IO_L2P_T0_34 Sch=JC4_P
|
||||||
|
#set_property -dict { PACKAGE_PIN U12 IOSTANDARD LVCMOS33 } [get_ports { jc_n[3] }]; #IO_L2N_T0_34 Sch=JC4_N
|
||||||
|
|
||||||
|
|
||||||
|
##Pmod Header JD
|
||||||
|
#set_property -dict { PACKAGE_PIN T14 IOSTANDARD LVCMOS33 } [get_ports { jd_p[0] }]; #IO_L5P_T0_34 Sch=JD1_P
|
||||||
|
#set_property -dict { PACKAGE_PIN T15 IOSTANDARD LVCMOS33 } [get_ports { jd_n[0] }]; #IO_L5N_T0_34 Sch=JD1_N
|
||||||
|
#set_property -dict { PACKAGE_PIN P14 IOSTANDARD LVCMOS33 } [get_ports { jd_p[1] }]; #IO_L6P_T0_34 Sch=JD2_P
|
||||||
|
#set_property -dict { PACKAGE_PIN R14 IOSTANDARD LVCMOS33 } [get_ports { jd_n[1] }]; #IO_L6N_T0_VREF_34 Sch=JD2_N
|
||||||
|
#set_property -dict { PACKAGE_PIN U14 IOSTANDARD LVCMOS33 } [get_ports { jd_p[2] }]; #IO_L11P_T1_SRCC_34 Sch=JD3_P
|
||||||
|
#set_property -dict { PACKAGE_PIN U15 IOSTANDARD LVCMOS33 } [get_ports { jd_n[2] }]; #IO_L11N_T1_SRCC_34 Sch=JD3_N
|
||||||
|
#set_property -dict { PACKAGE_PIN V17 IOSTANDARD LVCMOS33 } [get_ports { jd_p[3] }]; #IO_L21P_T3_DQS_34 Sch=JD4_P
|
||||||
|
#set_property -dict { PACKAGE_PIN V18 IOSTANDARD LVCMOS33 } [get_ports { jd_n[3] }]; #IO_L21N_T3_DQS_34 Sch=JD4_N
|
||||||
|
|
||||||
|
|
||||||
|
##Pmod Header JE
|
||||||
|
#set_property -dict { PACKAGE_PIN V12 IOSTANDARD LVCMOS33 } [get_ports { je[0] }]; #IO_L4P_T0_34 Sch=JE1
|
||||||
|
#set_property -dict { PACKAGE_PIN W16 IOSTANDARD LVCMOS33 } [get_ports { je[1] }]; #IO_L18N_T2_34 Sch=JE2
|
||||||
|
#set_property -dict { PACKAGE_PIN J15 IOSTANDARD LVCMOS33 } [get_ports { je[2] }]; #IO_25_35 Sch=JE3
|
||||||
|
#set_property -dict { PACKAGE_PIN H15 IOSTANDARD LVCMOS33 } [get_ports { je[3] }]; #IO_L19P_T3_35 Sch=JE4
|
||||||
|
#set_property -dict { PACKAGE_PIN V13 IOSTANDARD LVCMOS33 } [get_ports { je[4] }]; #IO_L3N_T0_DQS_34 Sch=JE7
|
||||||
|
#set_property -dict { PACKAGE_PIN U17 IOSTANDARD LVCMOS33 } [get_ports { je[5] }]; #IO_L9N_T1_DQS_34 Sch=JE8
|
||||||
|
#set_property -dict { PACKAGE_PIN T17 IOSTANDARD LVCMOS33 } [get_ports { je[6] }]; #IO_L20P_T3_34 Sch=JE9
|
||||||
|
#set_property -dict { PACKAGE_PIN Y17 IOSTANDARD LVCMOS33 } [get_ports { je[7] }]; #IO_L7N_T1_34 Sch=JE10
|
||||||
|
|
||||||
|
|
||||||
|
##USB-OTG overcurrent detect pin
|
||||||
|
#set_property -dict { PACKAGE_PIN U13 IOSTANDARD LVCMOS33 } [get_ports { otg_oc }]; #IO_L3P_T0_DQS_PUDC_B_34 Sch=OTG_OC
|
||||||
|
|
||||||
|
|
||||||
|
#VGA Connector
|
||||||
|
set_property -dict { PACKAGE_PIN M19 IOSTANDARD LVCMOS33 } [get_ports { io_r[0] }]; #IO_L7P_T1_AD2P_35 Sch=VGA_R1
|
||||||
|
set_property -dict { PACKAGE_PIN L20 IOSTANDARD LVCMOS33 } [get_ports { io_r[1] }]; #IO_L9N_T1_DQS_AD3N_35 Sch=VGA_R2
|
||||||
|
set_property -dict { PACKAGE_PIN J20 IOSTANDARD LVCMOS33 } [get_ports { io_r[2] }]; #IO_L17P_T2_AD5P_35 Sch=VGA_R3
|
||||||
|
set_property -dict { PACKAGE_PIN G20 IOSTANDARD LVCMOS33 } [get_ports { io_r[3] }]; #IO_L18N_T2_AD13N_35 Sch=VGA_R4
|
||||||
|
set_property -dict { PACKAGE_PIN F19 IOSTANDARD LVCMOS33 } [get_ports { io_r[4] }]; #IO_L15P_T2_DQS_AD12P_35 Sch=VGA_R5
|
||||||
|
set_property -dict { PACKAGE_PIN H18 IOSTANDARD LVCMOS33 } [get_ports { io_g[0] }]; #IO_L14N_T2_AD4N_SRCC_35 Sch=VGA_G0
|
||||||
|
set_property -dict { PACKAGE_PIN N20 IOSTANDARD LVCMOS33 } [get_ports { io_g[1] }]; #IO_L14P_T2_SRCC_34 Sch=VGA_G1
|
||||||
|
set_property -dict { PACKAGE_PIN L19 IOSTANDARD LVCMOS33 } [get_ports { io_g[2] }]; #IO_L9P_T1_DQS_AD3P_35 Sch=VGA_G2
|
||||||
|
set_property -dict { PACKAGE_PIN J19 IOSTANDARD LVCMOS33 } [get_ports { io_g[3] }]; #IO_L10N_T1_AD11N_35 Sch=VGA_G3
|
||||||
|
set_property -dict { PACKAGE_PIN H20 IOSTANDARD LVCMOS33 } [get_ports { io_g[4] }]; #IO_L17N_T2_AD5N_35 Sch=VGA_G4
|
||||||
|
set_property -dict { PACKAGE_PIN F20 IOSTANDARD LVCMOS33 } [get_ports { io_g[5] }]; #IO_L15N_T2_DQS_AD12N_35 Sch=VGA=G5
|
||||||
|
set_property -dict { PACKAGE_PIN P20 IOSTANDARD LVCMOS33 } [get_ports { io_b[0] }]; #IO_L14N_T2_SRCC_34 Sch=VGA_B1
|
||||||
|
set_property -dict { PACKAGE_PIN M20 IOSTANDARD LVCMOS33 } [get_ports { io_b[1] }]; #IO_L7N_T1_AD2N_35 Sch=VGA_B2
|
||||||
|
set_property -dict { PACKAGE_PIN K19 IOSTANDARD LVCMOS33 } [get_ports { io_b[2] }]; #IO_L10P_T1_AD11P_35 Sch=VGA_B3
|
||||||
|
set_property -dict { PACKAGE_PIN J18 IOSTANDARD LVCMOS33 } [get_ports { io_b[3] }]; #IO_L14P_T2_AD4P_SRCC_35 Sch=VGA_B4
|
||||||
|
set_property -dict { PACKAGE_PIN G19 IOSTANDARD LVCMOS33 } [get_ports { io_b[4] }]; #IO_L18P_T2_AD13P_35 Sch=VGA_B5
|
||||||
|
set_property -dict { PACKAGE_PIN P19 IOSTANDARD LVCMOS33 } [get_ports { io_hs }]; #IO_L13N_T2_MRCC_34 Sch=VGA_HS
|
||||||
|
set_property -dict { PACKAGE_PIN R19 IOSTANDARD LVCMOS33 } [get_ports { io_vs }]; #IO_0_34 Sch=VGA_VS
|
||||||
|
|
||||||
61
common/bitstream.vivado.tcl
Normal file
61
common/bitstream.vivado.tcl
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
# Input arguments
|
||||||
|
set DEVICE [lindex $argv 0]
|
||||||
|
set TOP [lindex $argv 1]
|
||||||
|
set REP [lindex $argv 2]
|
||||||
|
set TARGET [lindex $argv 3]
|
||||||
|
set MEM_FILE [file normalize [lindex $argv 4]]
|
||||||
|
|
||||||
|
source common/reportCriticalPaths.tcl
|
||||||
|
|
||||||
|
#lecture de tous les sv générés
|
||||||
|
set files [glob RTL/${REP}.${TOP}/*.sv]
|
||||||
|
read_verilog -sv $files
|
||||||
|
#ajout de clock à 10 MHz et 125 MHz
|
||||||
|
read_verilog -sv src/main/resources/vsrc/clk_wiz.v
|
||||||
|
read_verilog -sv src/main/resources/vsrc/dpram.v
|
||||||
|
|
||||||
|
set_param general.maxThreads 4
|
||||||
|
# Reading constraint file (.xdc file)
|
||||||
|
read_xdc common/${TOP}.xdc
|
||||||
|
|
||||||
|
# Detect XPM memory
|
||||||
|
auto_detect_xpm
|
||||||
|
|
||||||
|
# Start synthesis
|
||||||
|
synth_design -top ${TOP} -part ${DEVICE} -fanout_limit 100
|
||||||
|
#-flatten_hierarchy none
|
||||||
|
#-directive RuntimeOptimized
|
||||||
|
|
||||||
|
# Run logic optimization
|
||||||
|
opt_design
|
||||||
|
|
||||||
|
# Placing
|
||||||
|
place_design
|
||||||
|
#-directive Quick
|
||||||
|
#write_checkpoint -force place_design.dcp
|
||||||
|
|
||||||
|
# Routing
|
||||||
|
route_design -ultrathreads
|
||||||
|
# -directive Quick
|
||||||
|
#write_checkpoint -force route_design.dcp
|
||||||
|
|
||||||
|
# Reports
|
||||||
|
report_timing -file ${TOP}_timing.rpt
|
||||||
|
report_timing_summary -max_paths 500 -nworst 1 -input_pins -file ${TOP}_timing_summary.rpt
|
||||||
|
report_utilization -file ${TOP}_utilization_opt.rpt
|
||||||
|
report_critical_paths ${TOP}_critpath_report.csv
|
||||||
|
report_route_status -file ${TOP}_route_status.rpt
|
||||||
|
report_design_analysis -file ${TOP}_design_analysis.rpt
|
||||||
|
report_io -file ${TOP}_io_opt.rpt
|
||||||
|
report_drc -file ${TOP}_drc_route.rpt
|
||||||
|
report_clock_interaction -file ${TOP}_clock_interaction_opt.rpt
|
||||||
|
|
||||||
|
# Generate MMI map
|
||||||
|
write_mem_info -force ${TOP}.mmi
|
||||||
|
|
||||||
|
# Create bitstream
|
||||||
|
write_bitstream -force ${TARGET}
|
||||||
|
|
||||||
|
write_checkpoint -force bitstream_design.dcp
|
||||||
|
|
||||||
|
exit
|
||||||
5
common/clock.xdc
Normal file
5
common/clock.xdc
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Clock signal
|
||||||
|
create_clock -add -name sys_clk_pin -period 8.00 -waveform {0 4} [get_ports { clock }];
|
||||||
|
|
||||||
|
set_input_delay -clock sys_clk_pin 0.000 [all_inputs]
|
||||||
|
set_output_delay -clock sys_clk_pin 0.000 [all_outputs]
|
||||||
76
common/config.gtkw
Normal file
76
common/config.gtkw
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
[*]
|
||||||
|
[*] GTKWave Analyzer v3.3.116 (w)1999-2023 BSI
|
||||||
|
[*] Mon Nov 3 08:12:34 2025
|
||||||
|
[*]
|
||||||
|
[dumpfile] "/user/9/.base/mullero/home/cours/FMN/projet/build/chiselsim/ZzTopSpec/Simulation-pour-gtkwave/workdir-verilator/trace.vcd"
|
||||||
|
[dumpfile_mtime] "Mon Nov 3 08:11:19 2025"
|
||||||
|
[dumpfile_size] 59916
|
||||||
|
[savefile] "/user/9/.base/mullero/home/cours/FMN/projet/common/config.gtkw"
|
||||||
|
[timestart] 0
|
||||||
|
[size] 1920 1080
|
||||||
|
[pos] -67 -33
|
||||||
|
*-14.058537 52 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
|
||||||
|
[treeopen] TOP.
|
||||||
|
[treeopen] TOP.svsimTestbench.
|
||||||
|
[treeopen] TOP.svsimTestbench.dut.
|
||||||
|
[treeopen] TOP.svsimTestbench.dut.core.
|
||||||
|
[sst_width] 278
|
||||||
|
[signals_width] 142
|
||||||
|
[sst_expanded] 1
|
||||||
|
[sst_vpaned_height] 317
|
||||||
|
@200
|
||||||
|
-Instruction
|
||||||
|
@c04023
|
||||||
|
^>1 /matieres/3MMFMN/riscv32/bin/spike-dasm
|
||||||
|
TOP.svsimTestbench.dut.core.insn[31:0]
|
||||||
|
@29
|
||||||
|
(0)TOP.svsimTestbench.dut.core.insn[31:0]
|
||||||
|
(1)TOP.svsimTestbench.dut.core.insn[31:0]
|
||||||
|
(2)TOP.svsimTestbench.dut.core.insn[31:0]
|
||||||
|
(3)TOP.svsimTestbench.dut.core.insn[31:0]
|
||||||
|
(4)TOP.svsimTestbench.dut.core.insn[31:0]
|
||||||
|
(5)TOP.svsimTestbench.dut.core.insn[31:0]
|
||||||
|
(6)TOP.svsimTestbench.dut.core.insn[31:0]
|
||||||
|
(7)TOP.svsimTestbench.dut.core.insn[31:0]
|
||||||
|
(8)TOP.svsimTestbench.dut.core.insn[31:0]
|
||||||
|
(9)TOP.svsimTestbench.dut.core.insn[31:0]
|
||||||
|
(10)TOP.svsimTestbench.dut.core.insn[31:0]
|
||||||
|
(11)TOP.svsimTestbench.dut.core.insn[31:0]
|
||||||
|
(12)TOP.svsimTestbench.dut.core.insn[31:0]
|
||||||
|
(13)TOP.svsimTestbench.dut.core.insn[31:0]
|
||||||
|
(14)TOP.svsimTestbench.dut.core.insn[31:0]
|
||||||
|
(15)TOP.svsimTestbench.dut.core.insn[31:0]
|
||||||
|
(16)TOP.svsimTestbench.dut.core.insn[31:0]
|
||||||
|
(17)TOP.svsimTestbench.dut.core.insn[31:0]
|
||||||
|
(18)TOP.svsimTestbench.dut.core.insn[31:0]
|
||||||
|
(19)TOP.svsimTestbench.dut.core.insn[31:0]
|
||||||
|
(20)TOP.svsimTestbench.dut.core.insn[31:0]
|
||||||
|
(21)TOP.svsimTestbench.dut.core.insn[31:0]
|
||||||
|
(22)TOP.svsimTestbench.dut.core.insn[31:0]
|
||||||
|
(23)TOP.svsimTestbench.dut.core.insn[31:0]
|
||||||
|
(24)TOP.svsimTestbench.dut.core.insn[31:0]
|
||||||
|
(25)TOP.svsimTestbench.dut.core.insn[31:0]
|
||||||
|
(26)TOP.svsimTestbench.dut.core.insn[31:0]
|
||||||
|
(27)TOP.svsimTestbench.dut.core.insn[31:0]
|
||||||
|
(28)TOP.svsimTestbench.dut.core.insn[31:0]
|
||||||
|
(29)TOP.svsimTestbench.dut.core.insn[31:0]
|
||||||
|
(30)TOP.svsimTestbench.dut.core.insn[31:0]
|
||||||
|
(31)TOP.svsimTestbench.dut.core.insn[31:0]
|
||||||
|
@1401201
|
||||||
|
-group_end
|
||||||
|
@22
|
||||||
|
TOP.svsimTestbench.dut.core.pc[31:0]
|
||||||
|
TOP.svsimTestbench.dut.core.rd[4:0]
|
||||||
|
TOP.svsimTestbench.dut.core.rs1Value[31:0]
|
||||||
|
TOP.svsimTestbench.dut.core.rs2Value[31:0]
|
||||||
|
TOP.svsimTestbench.dut.core.wbVal[31:0]
|
||||||
|
@28
|
||||||
|
TOP.svsimTestbench.dut.core.clock
|
||||||
|
TOP.svsimTestbench.dut.core.reset
|
||||||
|
@22
|
||||||
|
TOP.svsimTestbench.dut.io_x31[31:0]
|
||||||
|
@28
|
||||||
|
TOP.svsimTestbench.dut.io_valid_x31
|
||||||
|
TOP.svsimTestbench.dut.core.rdValid
|
||||||
|
[pattern_trace] 1
|
||||||
|
[pattern_trace] 0
|
||||||
27
common/fix_mem.awk
Normal file
27
common/fix_mem.awk
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
BEGIN {
|
||||||
|
addr = 0
|
||||||
|
first = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Ligne adresse : @xxxx
|
||||||
|
/^@/ {
|
||||||
|
newAddr = strtonum("0x" substr($0, 2))
|
||||||
|
if (first) {
|
||||||
|
print $0 # garde le premier @xxxx (normalement @0000)
|
||||||
|
first = 0
|
||||||
|
} else {
|
||||||
|
while (addr < newAddr) {
|
||||||
|
print "00000000"
|
||||||
|
addr++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
next
|
||||||
|
}
|
||||||
|
|
||||||
|
# Ligne(s) de données
|
||||||
|
{
|
||||||
|
for (i = 1; i <= NF; i++) {
|
||||||
|
print $i
|
||||||
|
addr++
|
||||||
|
}
|
||||||
|
}
|
||||||
73
common/objtomem.awk
Executable file
73
common/objtomem.awk
Executable file
@@ -0,0 +1,73 @@
|
|||||||
|
# On utilise désormais objdump -s pour se faciliter la vie
|
||||||
|
# Contenu de la section .text :
|
||||||
|
# 1000 930f0000 930f7000 930fc0ff 93004000 ......p.......@.
|
||||||
|
# 1010 938f0000 938f3000 938fd0ff 1301c0ff ......0.........
|
||||||
|
# 1020 930f3100 930fd1ff 9301f07f 938f1100 ..1.............
|
||||||
|
# 1030 13020080 930ff2ff 13030001 930f0301 ................
|
||||||
|
# 1040 930300ff 938f03ff 37040080 1304f4ff ........7.......
|
||||||
|
# 1050 930f1400 9304f0ff 938f1400 938f2403 ..............$.
|
||||||
|
# 1060 eff05ffa .._.
|
||||||
|
# Contenu de la section .data :
|
||||||
|
# 1064 78563412 cacaefbe adde0000 00000000 xV4.............
|
||||||
|
# 1074 00000000 0000 ......
|
||||||
|
|
||||||
|
function swapbytes(drow) {
|
||||||
|
return substr(drow, 7, 2) substr(drow, 5, 2) substr(drow, 3, 2) substr(drow, 1, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
function writemem(addr, section) {
|
||||||
|
printf("@%08x\n", addr) # Adresse formatée
|
||||||
|
# On a aligné sur un multiple de 8, let's go
|
||||||
|
for (i = 1; i < length(section); i += 8) {
|
||||||
|
print(substr(section, i, 8));
|
||||||
|
}
|
||||||
|
print "deadc0de" # Balise de fin de section (constante)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Ligne donnant l'info de la section
|
||||||
|
# Toujours trouvée avant le reste
|
||||||
|
/^Contenu|^Contents/ {
|
||||||
|
if (content) {
|
||||||
|
writemem(start_addr, content)
|
||||||
|
}
|
||||||
|
# En toute logique on pourrait mettre là le nom de section, mais
|
||||||
|
# ça dépend de la locale, donc pour l'instant on laisse tomber
|
||||||
|
section = 1
|
||||||
|
content = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
/^\s*[0-9a-f]+/ {
|
||||||
|
if (section) {
|
||||||
|
addr = strtonum("0x" $1)
|
||||||
|
start_addr = strtonum("0x" $1) / 4
|
||||||
|
if (addr != start_addr * 4) {
|
||||||
|
print("Début de section non alignée : "addr) > "/dev/stderr"
|
||||||
|
exit
|
||||||
|
}
|
||||||
|
section = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 2; i <= NR; i++) {
|
||||||
|
v = $i
|
||||||
|
if (v ~ /[0-9a-f]{2,8}/) {
|
||||||
|
# Le dernier nombre peut avoir une taille 2, 4, 6, 8
|
||||||
|
if (length(v) == 6) {
|
||||||
|
v = v "00"
|
||||||
|
} else if (length($i) == 4) {
|
||||||
|
v = v "0000"
|
||||||
|
} else if (length($i) == 2) {
|
||||||
|
v = v "000000"
|
||||||
|
} else if (length($i) != 8) {
|
||||||
|
# Pour la partie avec les '.'
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
content = content swapbytes(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
END {
|
||||||
|
if (content) {
|
||||||
|
writemem(start_addr, content)
|
||||||
|
}
|
||||||
|
}
|
||||||
98
common/parse_report.py
Normal file
98
common/parse_report.py
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
def extractPrimitives(fileName, destName):
|
||||||
|
f = open(fileName, "r")
|
||||||
|
print("Needed primitives:")
|
||||||
|
print("------------------")
|
||||||
|
br = False
|
||||||
|
cnt = 0
|
||||||
|
while (not br):
|
||||||
|
line = f.readline()
|
||||||
|
if 'Primitives' in line:
|
||||||
|
br = (cnt == 1)
|
||||||
|
cnt += 1
|
||||||
|
f.readline()
|
||||||
|
f.readline()
|
||||||
|
line = f.readline()
|
||||||
|
while (line != "\n"):
|
||||||
|
print(line[:-1])
|
||||||
|
line = f.readline()
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
def extractUtilization(fileName, destName):
|
||||||
|
f = open(fileName, "r")
|
||||||
|
print("Real utilization:")
|
||||||
|
print("-----------------")
|
||||||
|
for line in f.readlines():
|
||||||
|
if 'Slice LUTs' in line:
|
||||||
|
number = line.split("|")[2].strip()
|
||||||
|
perc = line.split("|")[5].strip()
|
||||||
|
print("LUT6s : {} ({}%)".format(number, perc))
|
||||||
|
if 'Register as Flip Flop' in line:
|
||||||
|
number = line.split("|")[2].strip()
|
||||||
|
perc = line.split("|")[5].strip()
|
||||||
|
print("Flops : {} ({}%)".format(number, perc))
|
||||||
|
if ('Block RAM Tile' in line) and ('Note' not in line):
|
||||||
|
number = line.split("|")[2].strip()
|
||||||
|
perc = line.split("|")[5].strip()
|
||||||
|
print("BRAMs : {} ({}%)".format(number, perc))
|
||||||
|
if 'DSPs' in line:
|
||||||
|
number = line.split("|")[2].strip()
|
||||||
|
perc = line.split("|")[5].strip()
|
||||||
|
print("DSPs : {} ({}%)".format(number, perc))
|
||||||
|
f.close()
|
||||||
|
print("")
|
||||||
|
|
||||||
|
def extractPathDelay(fileName):
|
||||||
|
f = open(fileName, "r")
|
||||||
|
for line in f.readlines():
|
||||||
|
if 'Data Path Delay' in line:
|
||||||
|
delay = float(line.split(":")[1].strip().split(" ")[0].strip().split("ns")[0].strip())
|
||||||
|
f.close()
|
||||||
|
return delay
|
||||||
|
f.close()
|
||||||
|
print("Did not found any Data Path in circuit")
|
||||||
|
return float("NaN")
|
||||||
|
|
||||||
|
def extractClock(summaryFileName, timingFileName):
|
||||||
|
f = open(summaryFileName, "r")
|
||||||
|
clock = ''
|
||||||
|
slack = ''
|
||||||
|
for line in f.readlines():
|
||||||
|
if 'Clock' in line:
|
||||||
|
try:
|
||||||
|
clock = float(line.split("|")[1].strip())
|
||||||
|
except:
|
||||||
|
# Clock line was found in report, but not with a numeric value associated
|
||||||
|
# This is probably due to a circuit optimization where all sequential logic
|
||||||
|
# has been evinced - e.g. output stick to a constant, ...
|
||||||
|
pass
|
||||||
|
if 'Slack' in line:
|
||||||
|
try:
|
||||||
|
slack = float(line.split("|")[1].strip())
|
||||||
|
except:
|
||||||
|
# Same
|
||||||
|
pass
|
||||||
|
f.close()
|
||||||
|
print("Timing analysis:")
|
||||||
|
print("----------------")
|
||||||
|
if (clock == '' or slack == ''):
|
||||||
|
clock = float("NaN")
|
||||||
|
slack = float("NaN")
|
||||||
|
path = extractPathDelay(timingFileName)
|
||||||
|
else:
|
||||||
|
path = (clock-slack)
|
||||||
|
freq = (1.0/(path*pow(10, -9)))/pow(10,6)
|
||||||
|
print("Clock : %4.2f ns" % clock)
|
||||||
|
print("Slack : %4.2f ns" % slack)
|
||||||
|
print("Path : %4.2f ns" % path)
|
||||||
|
print("Freq : %4.2f MHz" % freq)
|
||||||
|
|
||||||
|
if __name__=="__main__":
|
||||||
|
if (len(sys.argv) != 4):
|
||||||
|
sys.exit("Usage python parse_report.py <utilizationReport> <timingReport> <destinationReport>")
|
||||||
|
print("\n--------- Résumé ----------\n")
|
||||||
|
extractClock(sys.argv[3], sys.argv[2])
|
||||||
|
extractUtilization(sys.argv[1], sys.argv[3])
|
||||||
|
extractPrimitives(sys.argv[1], sys.argv[3])
|
||||||
24
common/programFPGA.vivado.tcl
Normal file
24
common/programFPGA.vivado.tcl
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# Inputs arguments
|
||||||
|
set BIT_FILE [lindex $argv 0]
|
||||||
|
|
||||||
|
# Open hardware mode
|
||||||
|
open_hw
|
||||||
|
|
||||||
|
# Connect target
|
||||||
|
connect_hw_server
|
||||||
|
refresh_hw_server
|
||||||
|
set targets [get_hw_targets]
|
||||||
|
puts "targets: ${targets}"
|
||||||
|
current_hw_target [lindex ${targets} 0]
|
||||||
|
open_hw_target
|
||||||
|
|
||||||
|
# Select device
|
||||||
|
set devices [get_hw_devices]
|
||||||
|
puts "devices: ${devices}"
|
||||||
|
|
||||||
|
# Program
|
||||||
|
set_property PROGRAM.FILE ${BIT_FILE} [lindex [get_hw_devices] 1]
|
||||||
|
|
||||||
|
program_hw_devices
|
||||||
|
refresh_hw_device
|
||||||
|
|
||||||
4
common/programFPGA.xsdb.tcl
Normal file
4
common/programFPGA.xsdb.tcl
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
connect
|
||||||
|
target 4
|
||||||
|
fpga download.bit
|
||||||
|
exit
|
||||||
28
common/reportCriticalPaths.tcl
Normal file
28
common/reportCriticalPaths.tcl
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
proc report_critical_paths { filename } {
|
||||||
|
|
||||||
|
# Open the specified output file in write mode
|
||||||
|
set FH [open $filename w]
|
||||||
|
# Write the current date and CSV format to a file header
|
||||||
|
puts $FH "#\n# File created on [clock format [clock seconds]]\n#\n"
|
||||||
|
puts $FH "Startpoint,Endpoint,Slack,#Levels,#LUTs"
|
||||||
|
# Collect details from the 50 worst timing paths for the current analysis
|
||||||
|
# The $path variable contains a Timing Path object.
|
||||||
|
foreach path [get_timing_paths -max_paths 500 -nworst 1] {
|
||||||
|
# Get the LUT cells of the timing paths
|
||||||
|
set luts [get_cells -filter {REF_NAME =~ LUT*} -of_object $path]
|
||||||
|
# Get the startpoint of the Timing Path object
|
||||||
|
set startpoint [get_property STARTPOINT_PIN $path]
|
||||||
|
# Get the endpoint of the Timing Path object
|
||||||
|
set endpoint [get_property ENDPOINT_PIN $path]
|
||||||
|
# Get the slack on the Timing Path object
|
||||||
|
set slack [get_property SLACK $path]
|
||||||
|
# Get the number of logic levels between startpoint and endpoint
|
||||||
|
set levels [get_property LOGIC_LEVELS $path]
|
||||||
|
# Save the collected path details to the CSV file
|
||||||
|
puts $FH "$startpoint,$endpoint,$slack,$levels,[llength $luts]"
|
||||||
|
}
|
||||||
|
# Close the output file
|
||||||
|
close $FH
|
||||||
|
puts "CSV file $filename has been created.\n"
|
||||||
|
return 0
|
||||||
|
}; # End PROC
|
||||||
35
common/synthese.vivado.tcl
Normal file
35
common/synthese.vivado.tcl
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# Input arguments
|
||||||
|
set DEVICE [lindex $argv 0]
|
||||||
|
set TOP [lindex $argv 1]
|
||||||
|
set REP [lindex $argv 2]
|
||||||
|
|
||||||
|
#lecture de tous les sv générés
|
||||||
|
set files [glob RTL/${REP}.${TOP}/*.sv]
|
||||||
|
read_verilog -sv $files
|
||||||
|
read_verilog -sv src/main/resources/vsrc/clk_wiz.v
|
||||||
|
read_verilog -sv src/main/resources/vsrc/dpram.v
|
||||||
|
|
||||||
|
read_xdc common/clock.xdc
|
||||||
|
# Detect XPM memory
|
||||||
|
auto_detect_xpm
|
||||||
|
|
||||||
|
# Start synthesis
|
||||||
|
synth_design -top ${TOP} -part ${DEVICE} -mode "out_of_context"
|
||||||
|
report_utilization -file ${TOP}_utilization.rpt
|
||||||
|
report_timing -file ${TOP}_timing.rpt
|
||||||
|
report_clocks
|
||||||
|
|
||||||
|
get_ports *
|
||||||
|
|
||||||
|
set filename "${TOP}_summary.rpt"
|
||||||
|
set fileId [open $filename "w"]
|
||||||
|
if { [get_clocks] != "" } {
|
||||||
|
puts -nonewline $fileId "Clock | "
|
||||||
|
puts $fileId [get_property -min PERIOD [get_clocks]];
|
||||||
|
}
|
||||||
|
if { [get_timing_paths] != "" } {
|
||||||
|
puts -nonewline $fileId "Slack | "
|
||||||
|
puts $fileId [get_property SLACK [get_timing_paths]];
|
||||||
|
}
|
||||||
|
close $fileId
|
||||||
|
exit
|
||||||
1
project/build.properties
Normal file
1
project/build.properties
Normal file
@@ -0,0 +1 @@
|
|||||||
|
sbt.version = 1.9.7
|
||||||
1
project/plugins.sbt
Normal file
1
project/plugins.sbt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
logLevel := Level.Warn
|
||||||
200
src/main/resources/vsrc/clk_wiz.v
Normal file
200
src/main/resources/vsrc/clk_wiz.v
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
|
||||||
|
// file: design_1_clk_wiz_0_0.v
|
||||||
|
// (c) Copyright 2017-2018, 2023 Advanced Micro Devices, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
// This file contains confidential and proprietary information
|
||||||
|
// of AMD and is protected under U.S. and international copyright
|
||||||
|
// and other intellectual property laws.
|
||||||
|
//
|
||||||
|
// DISCLAIMER
|
||||||
|
// This disclaimer is not a license and does not grant any
|
||||||
|
// rights to the materials distributed herewith. Except as
|
||||||
|
// otherwise provided in a valid license issued to you by
|
||||||
|
// AMD, and to the maximum extent permitted by applicable
|
||||||
|
// law: (1) THESE MATERIALS ARE MADE AVAILABLE "AS IS" AND
|
||||||
|
// WITH ALL FAULTS, AND AMD HEREBY DISCLAIMS ALL WARRANTIES
|
||||||
|
// AND CONDITIONS, EXPRESS, IMPLIED, OR STATUTORY, INCLUDING
|
||||||
|
// BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, NON-
|
||||||
|
// INFRINGEMENT, OR FITNESS FOR ANY PARTICULAR PURPOSE; and
|
||||||
|
// (2) AMD shall not be liable (whether in contract or tort,
|
||||||
|
// including negligence, or under any other theory of
|
||||||
|
// liability) for any loss or damage of any kind or nature
|
||||||
|
// related to, arising under or in connection with these
|
||||||
|
// materials, including for any direct, or any indirect,
|
||||||
|
// special, incidental, or consequential loss or damage
|
||||||
|
// (including loss of data, profits, goodwill, or any type of
|
||||||
|
// loss or damage suffered as a result of any action brought
|
||||||
|
// by a third party) even if such damage or loss was
|
||||||
|
// reasonably foreseeable or AMD had been advised of the
|
||||||
|
// possibility of the same.
|
||||||
|
//
|
||||||
|
// CRITICAL APPLICATIONS
|
||||||
|
// AMD products are not designed or intended to be fail-
|
||||||
|
// safe, or for use in any application requiring fail-safe
|
||||||
|
// performance, such as life-support or safety devices or
|
||||||
|
// systems, Class III medical devices, nuclear facilities,
|
||||||
|
// applications related to the deployment of airbags, or any
|
||||||
|
// other applications that could lead to death, personal
|
||||||
|
// injury, or severe property or environmental damage
|
||||||
|
// (individually and collectively, "Critical
|
||||||
|
// Applications"). Customer assumes the sole risk and
|
||||||
|
// liability of any use of AMD products in Critical
|
||||||
|
// Applications, subject only to applicable laws and
|
||||||
|
// regulations governing limitations on product liability.
|
||||||
|
//
|
||||||
|
// THIS COPYRIGHT NOTICE AND DISCLAIMER MUST BE RETAINED AS
|
||||||
|
// PART OF THIS FILE AT ALL TIMES.
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
// User entered comments
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
// None
|
||||||
|
//
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
// Output Output Phase Duty Cycle Pk-to-Pk Phase
|
||||||
|
// Clock Freq (MHz) (degrees) (%) Jitter (ps) Error (ps)
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
// clk_out1__10.00000______0.000______50.0______197.700_____96.948
|
||||||
|
// clk_out2__125.00000______0.000______50.0______119.348_____96.948
|
||||||
|
//
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
// Input Clock Freq (MHz) Input Jitter (UI)
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
// __primary_________125.000____________0.010
|
||||||
|
|
||||||
|
`timescale 1ps/1ps
|
||||||
|
|
||||||
|
module clk_wiz
|
||||||
|
|
||||||
|
(// Clock in ports
|
||||||
|
// Clock out ports
|
||||||
|
output clk_out1,
|
||||||
|
output clk_out2,
|
||||||
|
// Status and control signals
|
||||||
|
input reset,
|
||||||
|
output locked,
|
||||||
|
input clk_in1
|
||||||
|
);
|
||||||
|
// Input buffering
|
||||||
|
//------------------------------------
|
||||||
|
wire clk_in1_design_1_clk_wiz_0_0;
|
||||||
|
wire clk_in2_design_1_clk_wiz_0_0;
|
||||||
|
IBUF clkin1_ibufg
|
||||||
|
(.O (clk_in1_design_1_clk_wiz_0_0),
|
||||||
|
.I (clk_in1));
|
||||||
|
|
||||||
|
// Clocking PRIMITIVE
|
||||||
|
//------------------------------------
|
||||||
|
|
||||||
|
// Instantiation of the MMCM PRIMITIVE
|
||||||
|
// * Unused inputs are tied off
|
||||||
|
// * Unused outputs are labeled unused
|
||||||
|
|
||||||
|
wire clk_out1_design_1_clk_wiz_0_0;
|
||||||
|
wire clk_out2_design_1_clk_wiz_0_0;
|
||||||
|
wire clk_out3_design_1_clk_wiz_0_0;
|
||||||
|
wire clk_out4_design_1_clk_wiz_0_0;
|
||||||
|
wire clk_out5_design_1_clk_wiz_0_0;
|
||||||
|
wire clk_out6_design_1_clk_wiz_0_0;
|
||||||
|
wire clk_out7_design_1_clk_wiz_0_0;
|
||||||
|
|
||||||
|
wire [15:0] do_unused;
|
||||||
|
wire drdy_unused;
|
||||||
|
wire psdone_unused;
|
||||||
|
wire locked_int;
|
||||||
|
wire clkfbout_design_1_clk_wiz_0_0;
|
||||||
|
wire clkfbout_buf_design_1_clk_wiz_0_0;
|
||||||
|
wire clkfboutb_unused;
|
||||||
|
wire clkout0b_unused;
|
||||||
|
wire clkout1b_unused;
|
||||||
|
wire clkout2_unused;
|
||||||
|
wire clkout2b_unused;
|
||||||
|
wire clkout3_unused;
|
||||||
|
wire clkout3b_unused;
|
||||||
|
wire clkout4_unused;
|
||||||
|
wire clkout5_unused;
|
||||||
|
wire clkout6_unused;
|
||||||
|
wire clkfbstopped_unused;
|
||||||
|
wire clkinstopped_unused;
|
||||||
|
wire reset_high;
|
||||||
|
|
||||||
|
MMCME2_ADV
|
||||||
|
#(.BANDWIDTH ("OPTIMIZED"),
|
||||||
|
.CLKOUT4_CASCADE ("FALSE"),
|
||||||
|
.COMPENSATION ("ZHOLD"),
|
||||||
|
.STARTUP_WAIT ("FALSE"),
|
||||||
|
.DIVCLK_DIVIDE (1),
|
||||||
|
.CLKFBOUT_MULT_F (8.000),
|
||||||
|
.CLKFBOUT_PHASE (0.000),
|
||||||
|
.CLKFBOUT_USE_FINE_PS ("FALSE"),
|
||||||
|
.CLKOUT0_DIVIDE_F (100.000),
|
||||||
|
.CLKOUT0_PHASE (0.000),
|
||||||
|
.CLKOUT0_DUTY_CYCLE (0.500),
|
||||||
|
.CLKOUT0_USE_FINE_PS ("FALSE"),
|
||||||
|
.CLKOUT1_DIVIDE (8),
|
||||||
|
.CLKOUT1_PHASE (0.000),
|
||||||
|
.CLKOUT1_DUTY_CYCLE (0.500),
|
||||||
|
.CLKOUT1_USE_FINE_PS ("FALSE"),
|
||||||
|
.CLKIN1_PERIOD (8.000))
|
||||||
|
mmcm_adv_inst
|
||||||
|
// Output clocks
|
||||||
|
(
|
||||||
|
.CLKFBOUT (clkfbout_design_1_clk_wiz_0_0),
|
||||||
|
.CLKFBOUTB (clkfboutb_unused),
|
||||||
|
.CLKOUT0 (clk_out1_design_1_clk_wiz_0_0),
|
||||||
|
.CLKOUT0B (clkout0b_unused),
|
||||||
|
.CLKOUT1 (clk_out2_design_1_clk_wiz_0_0),
|
||||||
|
.CLKOUT1B (clkout1b_unused),
|
||||||
|
.CLKOUT2 (clkout2_unused),
|
||||||
|
.CLKOUT2B (clkout2b_unused),
|
||||||
|
.CLKOUT3 (clkout3_unused),
|
||||||
|
.CLKOUT3B (clkout3b_unused),
|
||||||
|
.CLKOUT4 (clkout4_unused),
|
||||||
|
.CLKOUT5 (clkout5_unused),
|
||||||
|
.CLKOUT6 (clkout6_unused),
|
||||||
|
// Input clock control
|
||||||
|
.CLKFBIN (clkfbout_buf_design_1_clk_wiz_0_0),
|
||||||
|
.CLKIN1 (clk_in1_design_1_clk_wiz_0_0),
|
||||||
|
.CLKIN2 (1'b0),
|
||||||
|
// Tied to always select the primary input clock
|
||||||
|
.CLKINSEL (1'b1),
|
||||||
|
// Ports for dynamic reconfiguration
|
||||||
|
.DADDR (7'h0),
|
||||||
|
.DCLK (1'b0),
|
||||||
|
.DEN (1'b0),
|
||||||
|
.DI (16'h0),
|
||||||
|
.DO (do_unused),
|
||||||
|
.DRDY (drdy_unused),
|
||||||
|
.DWE (1'b0),
|
||||||
|
// Ports for dynamic phase shift
|
||||||
|
.PSCLK (1'b0),
|
||||||
|
.PSEN (1'b0),
|
||||||
|
.PSINCDEC (1'b0),
|
||||||
|
.PSDONE (psdone_unused),
|
||||||
|
// Other control and status signals
|
||||||
|
.LOCKED (locked_int),
|
||||||
|
.CLKINSTOPPED (clkinstopped_unused),
|
||||||
|
.CLKFBSTOPPED (clkfbstopped_unused),
|
||||||
|
.PWRDWN (1'b0),
|
||||||
|
.RST (reset_high));
|
||||||
|
assign reset_high = reset;
|
||||||
|
|
||||||
|
assign locked = locked_int;
|
||||||
|
// Clock Monitor clock assigning
|
||||||
|
//--------------------------------------
|
||||||
|
// Output buffering
|
||||||
|
//-----------------------------------
|
||||||
|
|
||||||
|
BUFG clkf_buf
|
||||||
|
(.O (clkfbout_buf_design_1_clk_wiz_0_0),
|
||||||
|
.I (clkfbout_design_1_clk_wiz_0_0));
|
||||||
|
|
||||||
|
BUFG clkout1_buf
|
||||||
|
(.O (clk_out1),
|
||||||
|
.I (clk_out1_design_1_clk_wiz_0_0));
|
||||||
|
|
||||||
|
|
||||||
|
BUFG clkout2_buf
|
||||||
|
(.O (clk_out2),
|
||||||
|
.I (clk_out2_design_1_clk_wiz_0_0));
|
||||||
|
|
||||||
|
endmodule
|
||||||
72
src/main/resources/vsrc/dpram.v
Normal file
72
src/main/resources/vsrc/dpram.v
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
// Template pour la lecture d'un fichier à la simulation et
|
||||||
|
// la génération de block rams à la synthèse
|
||||||
|
// https://docs.amd.com/r/en-US/ug901-vivado-synthesis/Byte-Write-Enable-True-Dual-Port-with-Byte-Wide-Write-Enable-Verilog
|
||||||
|
// True-Dual-Port BRAM with Byte-wide Write Enable
|
||||||
|
// Read-First mode
|
||||||
|
|
||||||
|
module dpram
|
||||||
|
#(
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
parameter NUM_COL = 4,
|
||||||
|
parameter COL_WIDTH = 8,
|
||||||
|
parameter ADDR_WIDTH = 10,
|
||||||
|
// Addr Width in bits : 2 *ADDR_WIDTH = RAM Depth
|
||||||
|
parameter DATA_WIDTH = NUM_COL*COL_WIDTH, // Data Width in bits
|
||||||
|
parameter INIT_FILE = "program.mem"
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
) (
|
||||||
|
input clkA,
|
||||||
|
input enaA,
|
||||||
|
input [NUM_COL-1:0] weA,
|
||||||
|
input [ADDR_WIDTH-1:0] addrA,
|
||||||
|
input [DATA_WIDTH-1:0] dinA,
|
||||||
|
output reg [DATA_WIDTH-1:0] doutA,
|
||||||
|
|
||||||
|
input clkB,
|
||||||
|
input enaB,
|
||||||
|
input [NUM_COL-1:0] weB,
|
||||||
|
input [ADDR_WIDTH-1:0] addrB,
|
||||||
|
input [DATA_WIDTH-1:0] dinB,
|
||||||
|
output reg [DATA_WIDTH-1:0] doutB
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// Core Memory
|
||||||
|
(* ram_style = "block" *) reg [DATA_WIDTH-1:0] ram_block [(2**ADDR_WIDTH)-1:0];
|
||||||
|
|
||||||
|
// Initialization
|
||||||
|
initial begin
|
||||||
|
if (INIT_FILE != "NONE") begin
|
||||||
|
$readmemh(INIT_FILE, ram_block);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
integer i;
|
||||||
|
// Port-A Operation
|
||||||
|
always @ (posedge clkA) begin
|
||||||
|
if(enaA) begin
|
||||||
|
for(i=0;i<NUM_COL;i=i+1) begin
|
||||||
|
if(weA[i]) begin
|
||||||
|
ram_block[addrA][i*COL_WIDTH +: COL_WIDTH] <= dinA[i*COL_WIDTH +: COL_WIDTH];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
doutA <= ram_block[addrA];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// Port-B Operation:
|
||||||
|
always @ (posedge clkB) begin
|
||||||
|
if(enaB) begin
|
||||||
|
for(i=0;i<NUM_COL;i=i+1) begin
|
||||||
|
if(weB[i]) begin
|
||||||
|
ram_block[addrB][i*COL_WIDTH +: COL_WIDTH] <= dinB[i*COL_WIDTH +: COL_WIDTH];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
doutB <= ram_block[addrB];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule // dpram
|
||||||
|
|
||||||
|
|
||||||
52
src/main/scala/common/EmitVerilog.scala
Normal file
52
src/main/scala/common/EmitVerilog.scala
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import chisel3._
|
||||||
|
import _root_.circt.stage.ChiselStage
|
||||||
|
|
||||||
|
|
||||||
|
object EmitModule {
|
||||||
|
def main(args: Array[String]): Unit = {
|
||||||
|
if (args.length < 1) {
|
||||||
|
println("Usage: mill ... common.EmitModule <ModuleName> [constructor args...]")
|
||||||
|
sys.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
val moduleName = args(0)
|
||||||
|
val ctorArgs = args.drop(1) // reste des arguments (strings)
|
||||||
|
|
||||||
|
// Reflection scala : charger dynamiquement un module
|
||||||
|
val cls = Class.forName(moduleName)
|
||||||
|
val ctor = cls.getConstructors.head
|
||||||
|
|
||||||
|
// Fonction de conversion string -> objet typé
|
||||||
|
def parseArg(s: String): AnyRef = {
|
||||||
|
// essai en Int
|
||||||
|
if (s.matches("^-?\\d+$")) {
|
||||||
|
Integer.valueOf(s.toInt)
|
||||||
|
}
|
||||||
|
// essai en Bool
|
||||||
|
else if (s.equalsIgnoreCase("true") || s.equalsIgnoreCase("false")) {
|
||||||
|
java.lang.Boolean.valueOf(s.toBoolean)
|
||||||
|
}
|
||||||
|
// sinon, on laisse en String
|
||||||
|
else {
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val generator = () => {
|
||||||
|
if (ctorArgs.isEmpty) {
|
||||||
|
ctor.newInstance().asInstanceOf[RawModule]
|
||||||
|
} else {
|
||||||
|
val argsObjects: Array[Object] = ctorArgs.map(parseArg).toArray
|
||||||
|
ctor.newInstance(argsObjects: _*).asInstanceOf[RawModule]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ChiselStage.emitSystemVerilogFile(
|
||||||
|
gen = generator(),
|
||||||
|
firtoolOpts = Array("-disable-all-randomization", "-strip-debug-info"),
|
||||||
|
args = Array("--target-dir", s"RTL/$moduleName")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
89
src/main/scala/projet/BusInterconnect.scala
Normal file
89
src/main/scala/projet/BusInterconnect.scala
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
package projet
|
||||||
|
|
||||||
|
import chisel3._
|
||||||
|
import chisel3.util._
|
||||||
|
|
||||||
|
// Bus très simple : au temps t le maître fait une requête
|
||||||
|
// au temps t+1, il a la réponse, sinon c'est mort !
|
||||||
|
|
||||||
|
// Signaux utilisés par le bus, coté esclave, on fait un
|
||||||
|
// Flip pour l'avoir coté maître ensuite
|
||||||
|
class BusInterface extends Bundle {
|
||||||
|
val addr = Input(UInt(32.W)) // adresse cible de la transaction
|
||||||
|
val wdata = Input(UInt(32.W)) // donnée en écriture (max 32 bits)
|
||||||
|
val rdata = Output(UInt(32.W)) // donnée lue de l'esclave, avec 1 cycle de retard
|
||||||
|
val be = Input(Vec(4, Bool())) // byte-enable pour les accès écriture mémoire
|
||||||
|
val en = Input(Bool()) // enable de la mémoire, read si be.orR est égal à 0, écriture sinon
|
||||||
|
}
|
||||||
|
|
||||||
|
class BusInterconnect extends Module {
|
||||||
|
val io = IO(new Bundle {
|
||||||
|
val master = new BusInterface // Bus avec un unique maître
|
||||||
|
val dmem = Flipped(new BusInterface) // et plusieurs esclaves
|
||||||
|
val ldip = Flipped(new BusInterface)
|
||||||
|
val clnt = Flipped(new BusInterface)
|
||||||
|
val vmem = Flipped(new BusInterface)
|
||||||
|
})
|
||||||
|
|
||||||
|
// On récupère l'adresse émise par le maître pour trouver à quel
|
||||||
|
// esclave elle fait référence
|
||||||
|
val addr = io.master.addr
|
||||||
|
|
||||||
|
// On reprend nos bonnes vielles adresses, pas très efficaces, mais ça fait le job
|
||||||
|
// -- Slave 0 1 2 3 4 5
|
||||||
|
// -- Name RAM prog | IP led | IP pin | IP PLIC | IP_CLINT | DDR
|
||||||
|
// BASE => ( X"0000_1000", X"3000_0000", X"3000_0008", X"0C00_0000", X"0200_0000", X"8000_0000"),
|
||||||
|
// HIGH => ( X"0000_8FFF", X"3000_0004", X"3000_0008", X"1000_0000", X"0200_C000", X"8FFF_FFFF")
|
||||||
|
|
||||||
|
val DMEM_BASE = 0x00000000L.U // Une mémoire unifiée code + données
|
||||||
|
val DMEM_SIZE = 0x00010000L.U
|
||||||
|
|
||||||
|
val LDIP_BASE = 0x30000000L.U
|
||||||
|
val LDIP_SIZE = 0x00000004L.U
|
||||||
|
|
||||||
|
val CLNT_BASE = 0x02000000L.U
|
||||||
|
val CLNT_SIZE = 0x0000C000L.U
|
||||||
|
|
||||||
|
val VMEM_BASE = 0x80000000L.U // Mémoire vidéo dual port
|
||||||
|
val VMEM_SIZE = 0x00020000L.U
|
||||||
|
|
||||||
|
val is_dmem = addr >= DMEM_BASE && addr < (DMEM_BASE + DMEM_SIZE)
|
||||||
|
val is_ldip = addr >= LDIP_BASE && addr < (LDIP_BASE + LDIP_SIZE)
|
||||||
|
val is_clnt = addr >= CLNT_BASE && addr < (CLNT_BASE + CLNT_SIZE)
|
||||||
|
val is_vmem = addr >= VMEM_BASE && addr < (VMEM_BASE + VMEM_SIZE)
|
||||||
|
|
||||||
|
// On connecte directement ce qui doit l'être en faisant l'hypothèse que
|
||||||
|
io.dmem.en := io.master.en && is_dmem
|
||||||
|
io.ldip.en := io.master.en && is_ldip
|
||||||
|
io.clnt.en := io.master.en && is_clnt
|
||||||
|
io.vmem.en := io.master.en && is_vmem
|
||||||
|
// la chose connectée à ses adresses qui commencent à zéro
|
||||||
|
io.dmem.addr := io.master.addr - DMEM_BASE
|
||||||
|
io.ldip.addr := io.master.addr - LDIP_BASE
|
||||||
|
io.clnt.addr := io.master.addr - CLNT_BASE
|
||||||
|
io.vmem.addr := io.master.addr - VMEM_BASE
|
||||||
|
io.dmem.wdata := io.master.wdata
|
||||||
|
io.ldip.wdata := io.master.wdata
|
||||||
|
io.clnt.wdata := io.master.wdata
|
||||||
|
io.vmem.wdata := io.master.wdata
|
||||||
|
io.dmem.be := io.master.be
|
||||||
|
io.ldip.be := io.master.be
|
||||||
|
io.clnt.be := io.master.be
|
||||||
|
io.vmem.be := io.master.be
|
||||||
|
|
||||||
|
// On doit décaler d'un cycle (latence de l'accès mémoire)
|
||||||
|
// les signaux qui multiplexent la donnée en sortie
|
||||||
|
val is_pdmem = RegNext(is_dmem)
|
||||||
|
val is_pldip = RegNext(is_ldip)
|
||||||
|
val is_pclnt = RegNext(is_clnt)
|
||||||
|
val is_pvmem = RegNext(is_vmem)
|
||||||
|
|
||||||
|
io.master.rdata := MuxCase(0x0BADADDL.U(32.W),
|
||||||
|
Seq(
|
||||||
|
is_pdmem -> io.dmem.rdata,
|
||||||
|
is_pldip -> io.ldip.rdata,
|
||||||
|
is_pclnt -> io.clnt.rdata,
|
||||||
|
is_pvmem -> io.vmem.rdata
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
56
src/main/scala/projet/CLint.scala
Normal file
56
src/main/scala/projet/CLint.scala
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
package projet
|
||||||
|
import chisel3._
|
||||||
|
|
||||||
|
/*\
|
||||||
|
* Principalement le timer pour pouvoir faire tourner space invaders !
|
||||||
|
\*/
|
||||||
|
class CLint extends Module {
|
||||||
|
val io = IO(new Bundle {
|
||||||
|
val bus = new BusInterface
|
||||||
|
val tip = Output(Bool())
|
||||||
|
})
|
||||||
|
|
||||||
|
// Adresses des deux registres 64 bits du composant
|
||||||
|
val MTIMERCMP_ADDR = 0x4000
|
||||||
|
val MTIMER_ADDR = 0xBFF8
|
||||||
|
|
||||||
|
// Sur le rv32i le timer est constitué de 2 registres pour faire 64 bits
|
||||||
|
val mtime_lo = RegInit(0.U(32.W))
|
||||||
|
val mtime_hi = RegInit(0.U(32.W))
|
||||||
|
|
||||||
|
// Free running, comme on dit chez nous !
|
||||||
|
mtime_lo := mtime_lo + 1.U
|
||||||
|
mtime_hi := Mux(mtime_lo + 1.U === 0.U, mtime_hi + 1.U, mtime_hi)
|
||||||
|
|
||||||
|
// La comparaison nécessite également 2 registres, pour une
|
||||||
|
// comparaison sur 64 bits si nécessaire
|
||||||
|
val mtimecmp_lo = RegInit(0.U(32.W))
|
||||||
|
val mtimecmp_hi = RegInit(0.U(32.W))
|
||||||
|
// Sortie mtimer interrupt pending dont on se moque pour l'instant
|
||||||
|
io.tip := (mtime_hi > mtimecmp_hi) || ((mtime_hi === mtimecmp_hi) && (mtime_lo >= mtimecmp_lo))
|
||||||
|
// Valeur par défaut sur le bus de lecture
|
||||||
|
// avec toujours un cycle de latence
|
||||||
|
val clintRegValue = RegInit(0.U(32.W))
|
||||||
|
|
||||||
|
io.bus.rdata := clintRegValue
|
||||||
|
|
||||||
|
when(io.bus.en === true.B) {
|
||||||
|
when (io.bus.be.asUInt.orR) { // Écriture
|
||||||
|
when(io.bus.addr === MTIMERCMP_ADDR.U) {
|
||||||
|
mtimecmp_lo := io.bus.wdata
|
||||||
|
} .elsewhen(io.bus.addr === (MTIMERCMP_ADDR + 4).U) {
|
||||||
|
mtimecmp_hi := io.bus.wdata
|
||||||
|
}
|
||||||
|
} .otherwise { // Lecture
|
||||||
|
when(io.bus.addr === MTIMER_ADDR.U) {
|
||||||
|
clintRegValue := mtime_lo
|
||||||
|
} .elsewhen(io.bus.addr === (MTIMER_ADDR + 4).U) {
|
||||||
|
clintRegValue := mtime_hi
|
||||||
|
} .elsewhen(io.bus.addr === MTIMERCMP_ADDR.U) {
|
||||||
|
clintRegValue := mtimecmp_lo
|
||||||
|
} .elsewhen(io.bus.addr === (MTIMERCMP_ADDR + 4).U) {
|
||||||
|
clintRegValue := mtimecmp_hi
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
64
src/main/scala/projet/ClockGen.scala
Normal file
64
src/main/scala/projet/ClockGen.scala
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
package projet
|
||||||
|
|
||||||
|
import chisel3._
|
||||||
|
import chisel3.util._
|
||||||
|
|
||||||
|
/*\
|
||||||
|
* Interface chisel du fichier généré par le clock_wizard
|
||||||
|
* de Vivado pour générer une clock à 10 MHz
|
||||||
|
* Le nom *doit* être celui qui est dans le systemverilog
|
||||||
|
* (qui est dans TP/common/clk_wiz_0_clk_wiz.sv)
|
||||||
|
\*/
|
||||||
|
class clk_wiz extends BlackBox with HasBlackBoxResource {
|
||||||
|
|
||||||
|
addResource("/vsrc/clk_wiz.v")
|
||||||
|
|
||||||
|
val io = IO(new Bundle {
|
||||||
|
val clk_in1 = Input(Clock())
|
||||||
|
val reset = Input(Reset())
|
||||||
|
val clk_out1 = Output(Clock()) // 10 MHz
|
||||||
|
val clk_out2 = Output(Clock()) // 125 MHz
|
||||||
|
val locked = Output(Bool())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
class ClockGen(val divBy: Int = 10, useSim: Boolean = false) extends Module {
|
||||||
|
val io = IO(new Bundle {
|
||||||
|
val clk_in1 = Input(Clock())
|
||||||
|
val reset = Input(Reset())
|
||||||
|
val clk_out1 = Output(Clock())
|
||||||
|
val clk_out2 = Output(Clock())
|
||||||
|
val locked = Output(Bool())
|
||||||
|
})
|
||||||
|
|
||||||
|
val maxCnt = (divBy - 1).U
|
||||||
|
val midCnt = ((divBy / 2) - 1).U
|
||||||
|
|
||||||
|
if (useSim) {
|
||||||
|
val cntReg = RegInit(0.U(log2Ceil(divBy).W))
|
||||||
|
cntReg := cntReg + 1.U
|
||||||
|
val outReg = RegInit(false.B)
|
||||||
|
val rstReg = RegInit(true.B)
|
||||||
|
|
||||||
|
rstReg := Mux(io.reset.asBool, true.B, rstReg)
|
||||||
|
|
||||||
|
io.locked := !rstReg
|
||||||
|
|
||||||
|
when (cntReg === midCnt) {
|
||||||
|
outReg := true.B
|
||||||
|
} .elsewhen (cntReg === maxCnt) {
|
||||||
|
outReg := false.B
|
||||||
|
cntReg := 0.U
|
||||||
|
rstReg := false.B
|
||||||
|
}
|
||||||
|
io.clk_out1 := outReg.asClock
|
||||||
|
io.clk_out2 := io.clk_in1
|
||||||
|
} else {
|
||||||
|
val ckgen = Module(new clk_wiz)
|
||||||
|
ckgen.io.clk_in1 := io.clk_in1
|
||||||
|
ckgen.io.reset := io.reset
|
||||||
|
io.clk_out1 := ckgen.io.clk_out1
|
||||||
|
io.clk_out2 := ckgen.io.clk_out2
|
||||||
|
io.locked := ckgen.io.locked
|
||||||
|
}
|
||||||
|
}
|
||||||
35
src/main/scala/projet/Compteur.scala
Normal file
35
src/main/scala/projet/Compteur.scala
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package projet
|
||||||
|
import chisel3._
|
||||||
|
|
||||||
|
// Déclaration d'un classe Compteur très générique
|
||||||
|
class Compteur(
|
||||||
|
n: Int, // Le paramètre n permet de fixer le nombre de bits du compteur.
|
||||||
|
hasEnable: Boolean = false, // Le paramètre hasEnable permet d'ajouter une entrée optionnelle pour autoriser l'incrémentation.
|
||||||
|
hasMax: Boolean = false // Le paramètre hasMax permet d'ajouter : - une entrée optionnelle max, qui fait reboucler le compteur après qu'il est atteint la valeur fournie.
|
||||||
|
// - une sortie optionnelle atMax, qui vaut 1 lorsque le compteur atteint la valeur max définie.
|
||||||
|
) extends Module {
|
||||||
|
val io = IO(new Bundle {
|
||||||
|
// Grâce au parametre hasEnable, on peut activer optionnellement le port d'entrée en.
|
||||||
|
// En scala, la classe option a 2 classes dérivées : Some et None.
|
||||||
|
// Some permet donc de wrapper notre type optionnel.
|
||||||
|
val en = if (hasEnable) Some(Input(Bool())) else None
|
||||||
|
val max = if (hasMax) Some(Input(UInt(n.W))) else None
|
||||||
|
val atMax = if (hasMax) Some(Output(Bool())) else None
|
||||||
|
val out = Output(UInt(n.W)) // On utilise le paramètre n pour remplacer la constante de l'exercice prédédent.
|
||||||
|
})
|
||||||
|
|
||||||
|
val compteur = RegInit(0.U(n.W))
|
||||||
|
|
||||||
|
// condition d'incrément
|
||||||
|
val doInc = if (hasEnable) io.en.get else true.B // Attention, notez qu'il faut utiliser l'opérateur get pour récupérer l'option wrappée par le Some.
|
||||||
|
|
||||||
|
|
||||||
|
// condition d'égalité au maximum
|
||||||
|
val atMax = if (hasMax) compteur === io.max.get else false.B
|
||||||
|
|
||||||
|
compteur := Mux(doInc, Mux(atMax, 0.U(n.W), compteur + 1.U), compteur)
|
||||||
|
|
||||||
|
if (hasMax) io.atMax.get := atMax
|
||||||
|
|
||||||
|
io.out := compteur
|
||||||
|
}
|
||||||
63
src/main/scala/projet/GeneSync.scala
Normal file
63
src/main/scala/projet/GeneSync.scala
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
package projet
|
||||||
|
import chisel3._
|
||||||
|
|
||||||
|
class GeneSync extends Module {
|
||||||
|
val io = IO(new Bundle {
|
||||||
|
val hsync = Output(Bool())
|
||||||
|
val vsync = Output(Bool())
|
||||||
|
val img = Output(Bool())
|
||||||
|
val x = Output(UInt(10.W))
|
||||||
|
val y = Output(UInt(9.W))
|
||||||
|
})
|
||||||
|
|
||||||
|
// Paramètres de synchronisation horizontale et verticale
|
||||||
|
val synchroX = VecInit(Seq(
|
||||||
|
95.U, // HS pulse width
|
||||||
|
47.U, // horizontal back porch
|
||||||
|
639.U, // horizontal display time
|
||||||
|
15.U // horizontal front porch
|
||||||
|
))
|
||||||
|
|
||||||
|
val synchroY = VecInit(Seq(
|
||||||
|
1.U, // VS pulse width
|
||||||
|
28.U, // vertical back porch
|
||||||
|
479.U, // vertical display time
|
||||||
|
9.U // vertical front porch
|
||||||
|
))
|
||||||
|
|
||||||
|
// Instanciation des compteurs
|
||||||
|
val compteurCLK = Module(new Compteur(3, hasMax = true)) // Compteur horloge (3 bits)
|
||||||
|
val comptX = Module(new Compteur(10, hasEnable = true, hasMax = true)) // Compteur horizontal (10 bits)
|
||||||
|
val comptY = Module(new Compteur(9, hasEnable = true, hasMax = true)) // Compteur vertical (9 bits)
|
||||||
|
val phaseH = Module(new Compteur(2, hasEnable = true)) // Phases horizontales (2 bits)
|
||||||
|
val phaseV = Module(new Compteur(2, hasEnable = true)) // Phases verticales (2 bits)
|
||||||
|
|
||||||
|
// Signaux d'activation
|
||||||
|
val en25 = compteurCLK.io.atMax.get // Activation à 25MHz quand compteurCLK atteint 4
|
||||||
|
val enPhaseH = en25 && comptX.io.atMax.get
|
||||||
|
val enY = enPhaseH && phaseH.io.out === 3.U // Nouvelle ligne
|
||||||
|
|
||||||
|
// Configuration compteur CLK (divise 125MHz par 5 pour avoir 25MHz)
|
||||||
|
compteurCLK.io.max.get := 4.U
|
||||||
|
|
||||||
|
// Configuration compteur X (horizontal)
|
||||||
|
comptX.io.en.get := en25
|
||||||
|
comptX.io.max.get := synchroX(phaseH.io.out)
|
||||||
|
|
||||||
|
// Configuration compteur Y (vertical)
|
||||||
|
comptY.io.en.get := enY
|
||||||
|
comptY.io.max.get := synchroY(phaseV.io.out)
|
||||||
|
|
||||||
|
// Configuration compteurs de phases
|
||||||
|
phaseH.io.en.get := enPhaseH
|
||||||
|
phaseV.io.en.get := enY && comptY.io.atMax.get
|
||||||
|
|
||||||
|
// Génération des signaux de sortie
|
||||||
|
io.hsync := phaseH.io.out.orR // true si phaseH != 0 (pas en phase pulse)
|
||||||
|
io.vsync := phaseV.io.out.orR // true si phaseV != 0 (pas en phase pulse)
|
||||||
|
io.img := (phaseH.io.out === 2.U) && (phaseV.io.out === 2.U) // Zone d'affichage
|
||||||
|
|
||||||
|
// Coordonnées pixel
|
||||||
|
io.x := comptX.io.out
|
||||||
|
io.y := comptY.io.out
|
||||||
|
}
|
||||||
164
src/main/scala/projet/IDMem.scala
Normal file
164
src/main/scala/projet/IDMem.scala
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
package projet
|
||||||
|
|
||||||
|
import chisel3._
|
||||||
|
import chisel3.util._
|
||||||
|
import chisel3.experimental._
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Blackbox Verilog pour la simu et la synthèse sans placement
|
||||||
|
*/
|
||||||
|
class dpram( // nom identique à celui du verilog
|
||||||
|
val NUM_COL: Int,
|
||||||
|
val COL_WIDTH: Int,
|
||||||
|
val ADDR_WIDTH: Int,
|
||||||
|
val DATA_WIDTH: Int,
|
||||||
|
val INIT_FILE: String = "program.mem"
|
||||||
|
) extends BlackBox(Map(
|
||||||
|
"NUM_COL" -> IntParam(NUM_COL),
|
||||||
|
"COL_WIDTH" -> IntParam(COL_WIDTH),
|
||||||
|
"ADDR_WIDTH" -> IntParam(ADDR_WIDTH),
|
||||||
|
"DATA_WIDTH" -> IntParam(DATA_WIDTH),
|
||||||
|
"INIT_FILE" -> StringParam(if (INIT_FILE != "") INIT_FILE else "NONE")
|
||||||
|
)) with HasBlackBoxResource {
|
||||||
|
|
||||||
|
require(DATA_WIDTH == NUM_COL * COL_WIDTH, "DATA_WIDTH must be equal to NUM_COL*COL_WIDTH.")
|
||||||
|
|
||||||
|
addResource("/vsrc/dpram.v")
|
||||||
|
|
||||||
|
val io = IO(new Bundle {
|
||||||
|
// Port A
|
||||||
|
val clkA = Input(Clock())
|
||||||
|
val enaA = Input(Bool())
|
||||||
|
val addrA = Input(UInt(ADDR_WIDTH.W))
|
||||||
|
val doutA = Output(UInt(DATA_WIDTH.W))
|
||||||
|
val dinA = Input(UInt(DATA_WIDTH.W))
|
||||||
|
val weA = Input(UInt(NUM_COL.W))
|
||||||
|
// Port B
|
||||||
|
val clkB = Input(Clock())
|
||||||
|
val enaB = Input(Bool())
|
||||||
|
val addrB = Input(UInt(ADDR_WIDTH.W))
|
||||||
|
val doutB = Output(UInt(DATA_WIDTH.W))
|
||||||
|
val dinB = Input(UInt(DATA_WIDTH.W))
|
||||||
|
val weB = Input(UInt(NUM_COL.W))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
class xpm_memory_tdpram( // nom identique à celui de Xilinx
|
||||||
|
val ADDR_WIDTH: Int,
|
||||||
|
val INIT_FILE: String
|
||||||
|
) extends BlackBox(Map(
|
||||||
|
"ADDR_WIDTH_A" -> IntParam(ADDR_WIDTH),
|
||||||
|
"ADDR_WIDTH_B" -> IntParam(ADDR_WIDTH),
|
||||||
|
"BYTE_WRITE_WIDTH_A" -> 8,
|
||||||
|
"MEMORY_INIT_FILE" -> StringParam(INIT_FILE),
|
||||||
|
"MEMORY_PRIMITIVE" -> "block",
|
||||||
|
"MEMORY_SIZE" -> 524288,
|
||||||
|
"READ_DATA_WIDTH_A" -> 32,
|
||||||
|
"READ_DATA_WIDTH_B" -> 32,
|
||||||
|
"READ_LATENCY_A" -> 1,
|
||||||
|
"READ_LATENCY_B" -> 1,
|
||||||
|
"WRITE_DATA_WIDTH_A" -> 32,
|
||||||
|
"WRITE_DATA_WIDTH_B" -> 32
|
||||||
|
)) with HasBlackBoxResource {
|
||||||
|
val io = IO(new Bundle {
|
||||||
|
// Port A
|
||||||
|
val clka = Input(Clock())
|
||||||
|
val rsta = Input(Bool())
|
||||||
|
val ena = Input(Bool())
|
||||||
|
val regcea = Input(Bool())
|
||||||
|
val dina = Input(UInt(32.W))
|
||||||
|
val addra = Input(UInt(ADDR_WIDTH.W))
|
||||||
|
val wea = Input(UInt(4.W))
|
||||||
|
val injectdbiterra = Input(Bool())
|
||||||
|
val injectsbiterra = Input(Bool())
|
||||||
|
val douta = Output(UInt(32.W))
|
||||||
|
// val dbiterrA = Output(Bool())
|
||||||
|
// val sbiterrA = Output(Bool())
|
||||||
|
// Port B
|
||||||
|
val clkb = Input(Clock())
|
||||||
|
val rstb = Input(Bool())
|
||||||
|
val enb = Input(Bool())
|
||||||
|
val regceb = Input(Bool())
|
||||||
|
val dinb = Input(UInt(32.W))
|
||||||
|
val addrb = Input(UInt(ADDR_WIDTH.W))
|
||||||
|
val web = Input(UInt(4.W))
|
||||||
|
val injectdbiterrb = Input(Bool())
|
||||||
|
val injectsbiterrb = Input(Bool())
|
||||||
|
val doutb = Output(UInt(32.W))
|
||||||
|
// val dbiterrB = Output(Bool())
|
||||||
|
// val sbiterrB = Output(Bool())
|
||||||
|
val sleep = Input(Bool())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/*\
|
||||||
|
* Attention, les BRAM sont adressables en mots, et les adresses émises
|
||||||
|
* sur notre interconnect sont des adresses octets, qui en revanche
|
||||||
|
* doivent être alignées sur le type de la donnée transportée.
|
||||||
|
* Nous optons ici pour une mémoire dual-port pour l'accès concurrent
|
||||||
|
* aux instructions et aux données, avec un espace d'adressage unique
|
||||||
|
* pour faciliter le préchargement (on espère).
|
||||||
|
*
|
||||||
|
\*/
|
||||||
|
class IDMem(sim: Boolean, addrWidth: Int, memFile: String ="") extends Module {
|
||||||
|
val io = IO(new Bundle {
|
||||||
|
val clki = Input(Clock())
|
||||||
|
val ibus = new BusInterface
|
||||||
|
val clkd = Input(Clock())
|
||||||
|
val dbus = new BusInterface
|
||||||
|
})
|
||||||
|
|
||||||
|
// Pour la simulation ou la synthèse ne nécessitant pas de repérer les BRAM, on blackboxe avec le verilog Xilinx
|
||||||
|
if (sim || memFile == "") {
|
||||||
|
// Mémoire 32 bits : 4 colonnes de 8 bit
|
||||||
|
// l'accès BRAM est un accès mot, et non octet, on ignore les 2 bits de
|
||||||
|
// poids faible de l'adresse
|
||||||
|
|
||||||
|
val unifiedRam = Module(new dpram(
|
||||||
|
NUM_COL = 4,
|
||||||
|
COL_WIDTH = 8,
|
||||||
|
ADDR_WIDTH = addrWidth - 2,
|
||||||
|
DATA_WIDTH = 32,
|
||||||
|
INIT_FILE = memFile
|
||||||
|
))
|
||||||
|
|
||||||
|
unifiedRam.io.clkA := io.clki
|
||||||
|
unifiedRam.io.clkB := io.clkd
|
||||||
|
// instructions sur le port a
|
||||||
|
unifiedRam.io.enaA := io.ibus.en
|
||||||
|
unifiedRam.io.addrA := io.ibus.addr(addrWidth - 1, 2)
|
||||||
|
io.ibus.rdata := unifiedRam.io.doutA
|
||||||
|
unifiedRam.io.weA := 0.U
|
||||||
|
unifiedRam.io.dinA := 0.U
|
||||||
|
// données sur le port b
|
||||||
|
unifiedRam.io.enaB := io.dbus.en
|
||||||
|
unifiedRam.io.addrB := io.dbus.addr(addrWidth - 1, 2)
|
||||||
|
io.dbus.rdata := unifiedRam.io.doutB
|
||||||
|
unifiedRam.io.weB := io.dbus.be.asUInt
|
||||||
|
unifiedRam.io.dinB := io.dbus.wdata
|
||||||
|
} else {
|
||||||
|
val dpram = Module(new xpm_memory_tdpram(ADDR_WIDTH = addrWidth - 2,INIT_FILE = memFile))
|
||||||
|
dpram.io.clka := clock
|
||||||
|
dpram.io.rsta := 0.U
|
||||||
|
dpram.io.ena := io.dbus.en
|
||||||
|
dpram.io.regcea := false.B
|
||||||
|
dpram.io.dina := io.dbus.wdata
|
||||||
|
dpram.io.addra := io.dbus.addr(addrWidth - 1, 2)
|
||||||
|
dpram.io.wea := io.dbus.be.asUInt
|
||||||
|
dpram.io.injectdbiterra := false.B
|
||||||
|
dpram.io.injectsbiterra := false.B
|
||||||
|
io.dbus.rdata := dpram.io.douta
|
||||||
|
|
||||||
|
dpram.io.clkb := clock
|
||||||
|
dpram.io.rstb := 0.U
|
||||||
|
dpram.io.enb := io.ibus.en
|
||||||
|
dpram.io.regceb := false.B
|
||||||
|
dpram.io.dinb := 0.U
|
||||||
|
dpram.io.addrb := io.ibus.addr(addrWidth - 1, 2)
|
||||||
|
dpram.io.web := 0.U
|
||||||
|
dpram.io.injectdbiterrb := false.B
|
||||||
|
dpram.io.injectsbiterrb := false.B
|
||||||
|
io.ibus.rdata := dpram.io.doutb
|
||||||
|
|
||||||
|
dpram.io.sleep := false.B
|
||||||
|
}
|
||||||
|
}
|
||||||
46
src/main/scala/projet/LedIp.scala
Normal file
46
src/main/scala/projet/LedIp.scala
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package projet
|
||||||
|
import chisel3._
|
||||||
|
import chisel3.util._
|
||||||
|
|
||||||
|
/*\
|
||||||
|
* Fusion de IP_LED et IP_PIN du projet VHDL
|
||||||
|
\*/
|
||||||
|
class LedIp extends Module {
|
||||||
|
val io = IO(new Bundle {
|
||||||
|
val x31 = Input(UInt(32.W))
|
||||||
|
val switch = Input(UInt(4.W))
|
||||||
|
val led = Output(UInt(4.W))
|
||||||
|
val button = Input(UInt(3.W))
|
||||||
|
val bus = new BusInterface
|
||||||
|
})
|
||||||
|
|
||||||
|
io.bus.rdata := 0x01ed01ed.U // On lira ça si on lit !
|
||||||
|
|
||||||
|
val dinReg = RegInit(0.U(32.W))
|
||||||
|
val x31Reg = RegNext(io.x31) // On lit x31 en continue
|
||||||
|
val btnReg = Reg(UInt(32.W))
|
||||||
|
|
||||||
|
// Le cpu fait une requête
|
||||||
|
// On se moque de l'adresse ...
|
||||||
|
// L'écriture écrit dans le registre, la lecture retourne l'état des boutons
|
||||||
|
when (io.bus.en === true.B) {
|
||||||
|
when (io.bus.be.asUInt.orR) { // Écriture dans le registre des leds internes
|
||||||
|
dinReg := io.bus.wdata
|
||||||
|
} .otherwise { // Lecture des boutons poussoirs, pas de débounce car lus par le soft
|
||||||
|
btnReg := Fill(29, 0.U) ## io.button
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
io.bus.rdata := btnReg
|
||||||
|
|
||||||
|
// On échantillonne la donnée d'entrée en continue
|
||||||
|
// C'est soit une donnée venant du registre x31 du
|
||||||
|
// proc, si le dernier switch est à 1, soit le registre écrit par le soft
|
||||||
|
val data = Mux(io.switch(3).asBool, x31Reg, dinReg)
|
||||||
|
|
||||||
|
// On l'affiche
|
||||||
|
io.led := MuxLookup(io.switch(2, 0), 0.U(4.W))(
|
||||||
|
(0 until 8).map { i =>
|
||||||
|
(i.U -> data((i * 4) + 3, i * 4))
|
||||||
|
})
|
||||||
|
}
|
||||||
14
src/main/scala/projet/Rv32i.scala
Normal file
14
src/main/scala/projet/Rv32i.scala
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package projet
|
||||||
|
|
||||||
|
import chisel3._
|
||||||
|
|
||||||
|
class Rv32i(sim: Boolean = true) extends Module {
|
||||||
|
val io = IO(new Bundle {
|
||||||
|
val ibus = Flipped(new BusInterface)
|
||||||
|
val dbus = Flipped(new BusInterface)
|
||||||
|
val x31 = Output(UInt(32.W))
|
||||||
|
val valid_x31 = if (sim) Some(Output(Bool())) else None
|
||||||
|
})
|
||||||
|
|
||||||
|
locally(sim)
|
||||||
|
}
|
||||||
40
src/main/scala/projet/VgaIp.scala
Normal file
40
src/main/scala/projet/VgaIp.scala
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
package projet
|
||||||
|
|
||||||
|
import chisel3._
|
||||||
|
import chisel3.util._
|
||||||
|
|
||||||
|
class VgaIp extends Module {
|
||||||
|
val io = IO(new Bundle {
|
||||||
|
val clk = Input(Clock())
|
||||||
|
val bus = Flipped(new BusInterface)
|
||||||
|
val hs = Output(Bool())
|
||||||
|
val vs = Output(Bool())
|
||||||
|
val r = Output(UInt(5.W))
|
||||||
|
val g = Output(UInt(6.W))
|
||||||
|
val b = Output(UInt(5.W))
|
||||||
|
})
|
||||||
|
|
||||||
|
withClock(io.clk) {
|
||||||
|
val sync = Module(new GeneSync())
|
||||||
|
io.hs := RegNext(sync.io.hsync)
|
||||||
|
io.vs := RegNext(sync.io.vsync)
|
||||||
|
val img_sync = RegNext(sync.io.img)
|
||||||
|
val index = RegNext(sync.io.x(2, 1))
|
||||||
|
|
||||||
|
io.bus.en := true.B
|
||||||
|
io.bus.be := VecInit(false.B, false.B, false.B, false.B)
|
||||||
|
io.bus.wdata := 0.U
|
||||||
|
val addr = 80.U * sync.io.y(8, 1) + sync.io.x(9, 3)
|
||||||
|
// Adresse mot dans la ram vidéo
|
||||||
|
io.bus.addr := addr(14, 0) ## 0.U(2.W)
|
||||||
|
|
||||||
|
val pixel = MuxCase(0.U,
|
||||||
|
(0 until 4).map {i =>
|
||||||
|
(index === i.U) -> io.bus.rdata(8 * (3 - i) + 7, 8 * (3 - i))
|
||||||
|
})
|
||||||
|
|
||||||
|
io.r := Mux(img_sync, pixel(7, 6)<<3, 0.U)
|
||||||
|
io.g := Mux(img_sync, pixel(5, 3)<<3, 0.U)
|
||||||
|
io.b := Mux(img_sync, pixel(2, 0)<<2, 0.U)
|
||||||
|
}
|
||||||
|
}
|
||||||
74
src/main/scala/projet/ZzTop.scala
Normal file
74
src/main/scala/projet/ZzTop.scala
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
package projet
|
||||||
|
|
||||||
|
import chisel3._
|
||||||
|
|
||||||
|
class ZzTop(file: String ="",sim: Boolean = true) extends Module {
|
||||||
|
val io = IO(new Bundle {
|
||||||
|
// Interface debug minimale
|
||||||
|
val switch = Input(UInt(4.W))
|
||||||
|
val led = Output(UInt(4.W))
|
||||||
|
val push = Input(UInt(3.W))
|
||||||
|
// Debug simu pour ne pas s'embeter avec le switch dans le testbench
|
||||||
|
val x31 = if (sim) Some(Output(UInt(32.W))) else None
|
||||||
|
val valid_x31 = if (sim) Some(Output(Bool())) else None
|
||||||
|
|
||||||
|
// Port vga
|
||||||
|
val hs = Output(Bool())
|
||||||
|
val vs = Output(Bool())
|
||||||
|
val r = Output(UInt(5.W))
|
||||||
|
val g = Output(UInt(6.W))
|
||||||
|
val b = Output(UInt(5.W))
|
||||||
|
})
|
||||||
|
|
||||||
|
val clkg = Module(new ClockGen(12, sim)) // ~ 10 MHz
|
||||||
|
// clock et reset sont les signaux implicites
|
||||||
|
clkg.io.clk_in1 := clock
|
||||||
|
clkg.io.reset := reset
|
||||||
|
val rst = (reset.asBool || !clkg.io.locked).asAsyncReset
|
||||||
|
|
||||||
|
// Ces deux IPs doivent tourner à 125 MHz
|
||||||
|
val vmem = Module(new IDMem(sim,17))
|
||||||
|
vmem.io.clki := clkg.io.clk_out2 // Clock du pixel
|
||||||
|
vmem.io.clkd := clkg.io.clk_out1 // Clock du reste du système
|
||||||
|
val vgad = Module(new VgaIp)
|
||||||
|
vgad.io.clk := clkg.io.clk_out2 // Clock pixel
|
||||||
|
|
||||||
|
withClockAndReset(clkg.io.clk_out1, rst) {
|
||||||
|
val interconnect = Module(new BusInterconnect)
|
||||||
|
val core = Module(new Rv32i(sim))
|
||||||
|
val dmem = Module(new IDMem(sim,16,file))
|
||||||
|
val ldip = Module(new LedIp)
|
||||||
|
val clnt = Module(new CLint)
|
||||||
|
|
||||||
|
dmem.io.clki := clkg.io.clk_out1 // Ou clock, non ?
|
||||||
|
dmem.io.clkd := clkg.io.clk_out1 //
|
||||||
|
|
||||||
|
ldip.io.switch := io.switch
|
||||||
|
ldip.io.button := io.push
|
||||||
|
ldip.io.x31 := core.io.x31
|
||||||
|
io.led := ldip.io.led
|
||||||
|
|
||||||
|
if (sim){
|
||||||
|
io.x31.get := core.io.x31
|
||||||
|
io.valid_x31.get := core.io.valid_x31.get
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
interconnect.io.master <> core.io.dbus
|
||||||
|
|
||||||
|
interconnect.io.dmem <> dmem.io.dbus
|
||||||
|
interconnect.io.ldip <> ldip.io.bus
|
||||||
|
interconnect.io.clnt <> clnt.io.bus
|
||||||
|
interconnect.io.vmem <> vmem.io.dbus
|
||||||
|
|
||||||
|
// connexion ad-hoc pour le bus d'instructions
|
||||||
|
dmem.io.ibus <> core.io.ibus
|
||||||
|
// connexion ad-hoc pour l'IP Vga
|
||||||
|
vmem.io.ibus <> vgad.io.bus
|
||||||
|
io.hs := vgad.io.hs
|
||||||
|
io.vs := vgad.io.vs
|
||||||
|
io.r := vgad.io.r
|
||||||
|
io.g := vgad.io.g
|
||||||
|
io.b := vgad.io.b
|
||||||
|
}
|
||||||
|
}
|
||||||
101
src/test/scala/projet/ZzTopSpec.scala
Normal file
101
src/test/scala/projet/ZzTopSpec.scala
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
package projet
|
||||||
|
|
||||||
|
import chisel3.simulator.scalatest.ChiselSim
|
||||||
|
import org.scalatest.freespec.AnyFreeSpec
|
||||||
|
import org.scalatest.matchers.must.Matchers
|
||||||
|
import org.scalatest.prop.TableDrivenPropertyChecks._
|
||||||
|
import java.nio.file.{Files, Paths}
|
||||||
|
import scala.jdk.CollectionConverters._
|
||||||
|
import scala.io.Source
|
||||||
|
import scala.collection.mutable.ListBuffer
|
||||||
|
|
||||||
|
class ZzTopSpec extends AnyFreeSpec with Matchers with ChiselSim {
|
||||||
|
|
||||||
|
def expectedValuesFromAsm(path: String): Seq[BigInt] = {
|
||||||
|
val line = Source.fromFile(path).getLines().find(_.startsWith("# expected:"))
|
||||||
|
line match {
|
||||||
|
case Some(l) =>
|
||||||
|
l.stripPrefix("# expected:")
|
||||||
|
.split(",")
|
||||||
|
.map(_.trim)
|
||||||
|
.filter(_.nonEmpty)
|
||||||
|
.map(str => BigInt(str, 16))
|
||||||
|
.toSeq
|
||||||
|
case None =>
|
||||||
|
Seq.empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def failTrace(msg: String = "Erreur",trace: ListBuffer[String]): Unit = {
|
||||||
|
val prefix = s"🛑 $msg\n"
|
||||||
|
val traceStr =
|
||||||
|
if (trace.nonEmpty) "Trace:\n" + trace.map(" - " + _).mkString("\n") + "\n"
|
||||||
|
else ""
|
||||||
|
fail(prefix + traceStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
def expectNextValue(dut: ZzTop, expected: BigInt, timeout: Int, trace: ListBuffer[String]): Unit = {
|
||||||
|
var cycles = 0
|
||||||
|
dut.clock.step(12) // Attente d'un cycle proc (12 cycles systeme)
|
||||||
|
// Attendre une écriture dans x31
|
||||||
|
while (!dut.io.valid_x31.get.peek().litToBoolean && cycles < timeout) {
|
||||||
|
dut.clock.step(12) // Attente d'un cycle proc
|
||||||
|
cycles += 1
|
||||||
|
trace += f"Attente $cycles cycles"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cycles < timeout){
|
||||||
|
// Vérifier la nouvelle valeur
|
||||||
|
val got = dut.io.x31.get.peek().litValue
|
||||||
|
trace += f"Valeur attendue = 0x$expected%08X, reçue = 0x$got%08X"
|
||||||
|
if ( got != expected ) failTrace("Mauvaise sortie",trace)
|
||||||
|
} else {
|
||||||
|
trace += f"Valeur attendue = 0x$expected%08X, reçue = ⏰"
|
||||||
|
failTrace("Simulation bloquée",trace)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Fonction de test commune
|
||||||
|
def testWith(file: String): Unit = {
|
||||||
|
val path = file.replace("/mem/","/").replace(".mem",".s")
|
||||||
|
if (Files.exists(Paths.get(path))){
|
||||||
|
val expected = expectedValuesFromAsm(path)
|
||||||
|
simulate(new ZzTop(file,true)) { dut =>
|
||||||
|
val trace = ListBuffer[String]()
|
||||||
|
expected.foreach { value =>
|
||||||
|
expectNextValue(dut, value, timeout = 100,trace)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val dir = sys.env("PROOT")+"/bench/mem/"
|
||||||
|
val suffix = ".mem"
|
||||||
|
val programFile = sys.env.get("PROG")
|
||||||
|
if (programFile.isDefined) {
|
||||||
|
val name = programFile.get
|
||||||
|
val path = dir+name+suffix
|
||||||
|
|
||||||
|
"Simulation pour gtkwave" in {
|
||||||
|
simulate(new ZzTop(path,true)) { dut =>
|
||||||
|
dut.io.switch.poke(8) // Paramétrage de l'affichage
|
||||||
|
dut.io.led.expect(dut.io.led.peek()) // test dummy
|
||||||
|
dut.clock.step(sys.env("CYCLES").toInt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s"Test unitaire $name" in testWith(path)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Récupération des fichiers .mem et génération de la table de programmes
|
||||||
|
val programs = Files.list(Paths.get(dir)).iterator().asScala.filter(_.toString.endsWith(suffix))
|
||||||
|
val p2 = programs.map(path => (path.getFileName.toString.stripSuffix(suffix), path.toString)).toSeq
|
||||||
|
val table = Table(("name", "path"), p2: _*)
|
||||||
|
|
||||||
|
// Déclaration des tests à partir de la table
|
||||||
|
forAll(table) { (name, path) =>
|
||||||
|
s"Programme $name" in testWith(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user