Squelette du projet

This commit is contained in:
Olivier Muller
2025-11-03 11:00:11 +01:00
commit ff440776bb
41 changed files with 2522 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
RTL
out
build
mill*
.prog*
bench/mem

1
.mill-jvm-opts Normal file
View File

@@ -0,0 +1 @@
-Dchisel.project.root=${PWD}

1
.mill-version Normal file
View File

@@ -0,0 +1 @@
0.12.5

186
Makefile Normal file
View 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),)

View 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.

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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
View 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
View 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
View 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
View 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
View 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])

View 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

View File

@@ -0,0 +1,4 @@
connect
target 4
fpga download.bit
exit

View 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

View 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
View File

@@ -0,0 +1 @@
sbt.version = 1.9.7

1
project/plugins.sbt Normal file
View File

@@ -0,0 +1 @@
logLevel := Level.Warn

View 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

View 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

View 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")
)
}
}

View 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
)
)
}

View 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
}
}
}
}

View 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
}
}

View 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
}

View 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
}

View 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
}
}

View 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))
})
}

View 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)
}

View 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)
}
}

View 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
}
}

View 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)
}
}
}