1. El principio básico de la comunicación SPI
1. Introducción a la comunicación SPI
SPI (Interfaz periférica en serie, Interfaz periférica en serie) es una tecnología de interfaz en serie sincrónica introducida por Motorola. El bus SPI se implementa físicamente mediante un módulo llamado puerto serie síncrono (Synchronous Serial Port) en la unidad de control del microprocesador (MCU) conectado al microcontrolador del dispositivo periférico (PICmicro), que permite que la MCU realice comunicaciones de datos de alta velocidad con varios periféricos. dispositivos en modo serial síncrono full-duplex.
SPI se utiliza principalmente entre EEPROM, Flash, reloj en tiempo real (RTC), convertidor de digital a analógico (ADC), procesador de señal digital (DSP) y decodificador de señal digital. Solo ocupa cuatro pines (Pin) en el chip para control y transmisión de datos, lo que ahorra la cantidad de pines del chip y ahorra espacio para el diseño de PCB. Es precisamente por esta función fácil de usar que cada vez más chips tienen tecnología SPI integrada.
SPI tiene principalmente las siguientes características:
- El método de control de modo maestro-esclavo (maestro-esclavo)
SPI estipula que la comunicación entre dos dispositivos SPI debe ser controlada por el dispositivo maestro (maestro) para controlar el dispositivo esclavo (esclavo).Un dispositivo maestro puede proporcionar reloj y chip de dispositivo esclavo selección (Slave Select) para controlar varios dispositivos Esclavo. El protocolo SPI también estipula que el reloj del dispositivo Esclavo es proporcionado al dispositivo Esclavo por el dispositivo Maestro a través del pin SCK. El dispositivo Esclavo en sí no puede generar o controlar el Reloj. Sin el Reloj, el dispositivo Esclavo no puede funcionar normalmente. .
-
Uso del modo síncrono (Synchronous) para transmitir datos
El dispositivo maestro generará el pulso de reloj correspondiente (Pulso de reloj) de acuerdo con los datos que se van a intercambiar, el pulso de reloj constituye la señal de reloj (Señal de reloj), la señal de reloj pasa la polaridad del reloj (CPOL) y la fase del reloj (CPHA) controla cuándo se intercambian datos entre dos dispositivos SPI y cuándo se muestrean los datos recibidos para garantizar que los datos se transmitan sincrónicamente entre los dos dispositivos. -
Intercambios de datos
La razón por la que la transmisión de datos entre dispositivos SPI también se denomina intercambio de datos se debe a que el protocolo SPI estipula que un dispositivo SPI no puede actuar únicamente como "Transmisor" o "Receptor" en el proceso de comunicación de datos (Receptor)”. cada período de reloj, el dispositivo SPI enviará y recibirá datos de tamaño de bits, lo que significa que el dispositivo ha intercambiado datos de tamaño de bits.Si un dispositivo esclavo puede recibir los datos enviados por el maestro, la señal de control debe poder
ser accedido por el dispositivo Maestro antes de este (Acceso). Por lo tanto, el dispositivo Maestro primero debe seleccionar el dispositivo Esclavo a través del pin SS/CS, y seleccionar el dispositivo Esclavo al que desea acceder. En el proceso de transmisión de datos En , cada
recibido los datos deben ser muestreados antes de la próxima transmisión de datos. Si los datos recibidos previamente no se leen, entonces los datos que se han recibido pueden descartarse, lo que resulta en la falla final del módulo físico SPI. Por lo tanto, en el programa, los datos en el dispositivo SPI generalmente se lee después de que el SPI transfiere los datos, incluso si los datos (datos ficticios) son inútiles en nuestro programa.
2. Reglas de comunicación del módulo maestro-esclavo SPI
La figura es una descripción simple de la comunicación entre dispositivos SPI, expliquemos los varios componentes (Módulo) que se muestran en la figura:
SSPBUF, búfer de puerto serie síncrono, generalmente se refiere al búfer interno en el dispositivo SPI, generalmente en forma de FIFO físicamente, que almacena datos temporales durante la transmisión;
SSPSR, Synchronous Serial Port Register, generalmente se refiere al registro de desplazamiento (Shift Regitser) en el dispositivo SPI, su función es mover datos dentro o fuera de SSPBUF de acuerdo con el ancho de bits de datos establecido (ancho de bits);
Controlador, generalmente se refiere a los registros de control en el dispositivo SPI, puede configurarlos para establecer el modo de transmisión del bus SPI.
Normalmente, solo necesitamos programar los cuatro pines (pines) descritos anteriormente para controlar la comunicación de datos entre todo el dispositivo SPI:
SCK, Serial Clock, la función principal es transmitir la señal del reloj desde el dispositivo Maestro al dispositivo Esclavo, y controlar el tiempo y la tasa de intercambio de datos;
SS/CS, Selección de esclavo/Selección de chip, utilizado para la selección del chip del dispositivo maestro Dispositivo esclavo, de modo que el dispositivo maestro pueda acceder al dispositivo esclavo seleccionado;
SDO/MOSI, Salida de datos en serie/Salida principal Esclavo de entrada, también conocido como Tx-Channel en el Maestro, como exportación de datos, utilizado principalmente para equipos SPI para enviar datos;
SDI/MISO, Entrada de datos en serie/Entrada maestra Salida esclava, también conocida como Rx-Channel en el maestro, como entrada de datos, utilizada principalmente para que los dispositivos SPI reciban datos;
Durante el proceso de comunicación del dispositivo SPI, se generará un bucle de enlace de datos (Data Loop) entre el dispositivo Maestro y el dispositivo Esclavo, como se muestra en la figura anterior, a través de los pines SDO y SDI, el SSPSR controla los datos que se van a enviar. se mueve dentro y fuera del SSPBUF, el controlador determina el modo de comunicación del bus SPI y el SCK transmite la señal del reloj.
-
Temporización
Primero, explique dos conceptos aquí:
CPOL: polaridad del reloj, que indica si la señal del reloj es alta o baja cuando el SPI está inactivo. Si CPOL está configurado en 1, entonces el dispositivo está debajo del pin SCK cuando está inactivo. El pin SCK es alto. Cuando CPOL se establece en 0, es todo lo contrario.
CPHA: Fase de reloj, lo que indica que el dispositivo SPI activa el muestreo de datos cuando la señal del reloj en el pin SCK se convierte en un flanco ascendente, o cuando la señal del reloj cambia Activa el muestreo de datos cuando el flanco descendente. Si CPHA se establece en 1, el dispositivo SPI activa el muestreo de datos cuando la señal del reloj se convierte en un flanco descendente y envía datos cuando el flanco ascendente. Cuando CPHA se establece en 0, es todo lo contrario. Este
ejemplo El modo de transmisión de datos SPI utilizado se establece en CPOL = 1, CPHA = 1. De esta manera, dentro de un período de Reloj, cada dispositivo SPI individual puede enviar y recibir al mismo tiempo en un modo dúplex completo (Full-Duplex ) forma de datos de 1 bit, lo que equivale a intercambiar datos de tamaño de 1 bit. Si el ancho de canal del bus SPI se establece en Byte, significa que la unidad mínima de cada transmisión de datos en el bus SPI es Byte, entonces el dispositivo montado en el bus SPI Cada proceso de transmisión de datos requiere al menos 8 ciclos de reloj (ignorando el retraso físico del dispositivo). Por lo tanto, cuanto más rápida sea la frecuencia del bus SPI y más corto el ciclo de reloj, más rápida será la tasa de intercambio de datos entre dispositivos SPI. -
SSPSR
SSPSR es el registro de desplazamiento (Shift Register) dentro del dispositivo SPI. Su función principal es mover datos dentro o fuera del SSPBUF de acuerdo con el estado de la señal de reloj SPI. El tamaño de los datos que se mueven cada vez está determinado por el Bus-Width y Channel-Width La función de Bus-Width es especificar la unidad
de transmisión de datos entre el bus de direcciones y el dispositivo maestro.
Por ejemplo, queremos escribir datos de 16 bytes en el SSPBUF en el dispositivo maestro: Primero, configure el ancho de bus para el registro de configuración del dispositivo maestro. El ancho es byte; luego escriba datos en el registro de desplazamiento Tx-Data del dispositivo maestro en la entrada del bus de direcciones, y escriba datos de 1 byte cada vez (usando la función writeb); después de escribir datos de 1 byte, los datos en el dispositivo maestro El registro de desplazamiento Tx-Data cambiará automáticamente los datos de 1 byte del bus de direcciones al SSPBUF; las acciones anteriores deben repetirse 16 veces. de
ancho de canal es especificar la unidad de transmisión de datos entre el dispositivo maestro y el dispositivo esclavo. Similar al ancho de bus, el registro de desplazamiento dentro del dispositivo maestro transferirá automáticamente los datos del maestro-SSPBUF al esclavo-SDI pin en el dispositivo Esclavo a través del pin Maestro-SDO de acuerdo con el Ancho del Canal, y luego el Esclavo-SSPSR Cada dato recibido se mueve al Esclavo-SSPBUF. Normalmente, el Ancho del
Bus siempre será mayor o igual que el Ancho del Canal , para garantizar que la frecuencia de intercambio de datos entre el maestro y el esclavo sea mayor que la del bus de direcciones y el maestro. La frecuencia del intercambio de datos entre ellos debe ser rápida, lo que da como resultado que los datos almacenados en SSPBUF no sean válidos. . -
SSPBUF
Sabemos que en cada ciclo de reloj, los datos intercambiados entre Maestro y Esclavo son en realidad copiados de SSPBUF por el registro de desplazamiento interno SPI.Podemos pasar al registro correspondiente a SSPBUF (registro Tx-Data / Rx-Data) para leer y escribir datos y manipular indirectamente el SSPBUF dentro del dispositivo SPI.
Por ejemplo, antes de enviar datos, primero debemos escribir los datos que se enviarán al registro Tx-Data del Maestro, y estos datos serán transferidos por el Maestro-SSPSR registro de desplazamiento De acuerdo con el ancho del bus, los datos se mueven automáticamente al maestro-SSPBUF, y luego los datos se moverán fuera del maestro-SSPBUF de acuerdo con el ancho del canal por el maestro-SSPSR y se pasarán al esclavo -SDI pin a través del pin Master-SDO, y Slave-SSPSR transferirá Los datos recibidos de Slave-SDI se mueven a Slave-SSPBUF Al mismo tiempo, los datos en Slave-SSPBUF se envían a Master-SDI, Master -SSPSR a través de Slave-SDO de acuerdo con el tamaño de cada dato recibido (Channel-Width). Luego mueva los datos recibidos de Master-SDI a Master-SSPBUF. Después de completar una sola transmisión de datos, el programa de usuario puede leer los datos intercambiados por el dispositivo Maestro desde el registro Rx-Data del dispositivo Maestro. -
Controlador El
Controlador en el dispositivo Maestro controla principalmente el dispositivo Esclavo a través de la señal de reloj (Clock Signal) y la señal de selección de chip (Señal de Selección Esclavo).El dispositivo Esclavo esperará hasta que reciba la señal de selección de chip enviada por el dispositivo Maestro, y luego, de acuerdo con la señal del reloj para trabajar.La
operación de selección de chip del dispositivo maestro debe ser implementada por el programa.Por ejemplo: el programa baja la señal del reloj del pin SS/CS para completar el trabajo preliminar de los datos del dispositivo SPI comunicación; cuando el programa quiere que el dispositivo SPI finalice los datos Durante la comunicación, lleve la señal del reloj en el pin SS/CS a un nivel alto.
2. Caso del módulo maestro-esclavo SPI e implementación de FPGA
1. Descripción del caso
Para diseñar un módulo maestro SPI de 4 hilos y un módulo esclavo, los requisitos son los siguientes:
- Definición de la interfaz del módulo host (maestro):
module spi_master(
input clk_40k, //时钟信号,40kHz
input rst_n, //复位信号,低有效
input [7:0] data_in, //主机准备要输出给从机的数据,8位宽
input send_start, //通信使能信号,高有效,宽度为1个时钟周期(40kHz),收到该信号后开始一次主从设备通信。
output [7:0] data_out, //主机从从机接收到的数据,8位宽
output data_out_vld, //输出数据有效标志,高电平有效,宽度为1个时钟周期(40kHz)
output cs_n, //从设备片选使能信号,低有效,低电平时选中从设备与主设备进行通信,处于通信状态时维持低电平。
output sclk, //同步时钟,1kHz,空闲时置低电平
input miso, //主机当前从从机收到的串行数据
output mosi //主机当前发送给从机的串行数据
);
- Definición de la interfaz del módulo esclavo (esclavo):
module spi_slave(
input rst_n, //复位信号,低有效
input cs_n, //从设备片选使能信号
input sclk, //SPI时钟,1kHz空闲时置低电平,
input mosi, //从机从主机接收到的串行数据
output miso, //从机要发送给主机的串行数据
output [7:0] reg0_out, //内部寄存器0的值
output [7:0] reg1_out, //内部寄存器1的值
output [7:0] reg2_out, //内部寄存器2的值
output [7:0] reg3_out //内部寄存器3的值
);
- Descripción de la función del circuito:
Hay cuatro registros internos de ocho bits (reg0, reg1, reg2, reg3) en el módulo esclavo, y las direcciones son respectivamente 0 ~ 3. El módulo maestro configura los valores de los cuatro registros en el módulo esclavo a través del SPI bus, y el valor del registro esclavo se pasa directamente a través de su puerto.
Después de que el módulo maestro recibe la señal send_start, envía los datos data_in al reg0 del módulo esclavo a través del bus spi, luego cambia data_in a la derecha dos bits y lo envía al reg1 del módulo esclavo, y luego cambia data_in a la derecha por dos bits y lo envía a reg2 Finalmente, data_in se recircula a la derecha por dos bits y se envía a reg3. Hasta el momento, el maestro ha completado la configuración de todos los registros en el esclavo. Luego, el maestro lee los datos de reg3 en el esclavo a través del bus spi, los envía a través de data_out y proporciona un data_out_vld de ancho de ciclo al mismo tiempo.
- Formato de transmisión SPI:
Cada cuadro de datos SPI contiene 16 bits. El bit 0 que se envía primero es el bit de control de lectura y escritura. Si el bit es 0, significa que el maestro escribe datos en el esclavo, y si es 1, significa que el maestro lee datos del esclavo; los bits 1-7 se envían más tarde Es el bit de dirección, envía primero la dirección alta y luego la dirección baja, 9-16 bits son los bits de datos, y los datos de bit alto se envían primero. Todos los datos se generan en el borde ascendente de sclk y se muestrean en el borde descendente.
El formato de datos de escritura SPI se muestra en la Figura 1:
El formato de datos de lectura SPI se muestra en la Figura 2:
2. Ideas de diseño
- El protocolo SPI está determinado tanto por el maestro como por el esclavo. Aquí, se requiere que el módulo maestro envíe 5 grupos de números cada vez. Los primeros cuatro grupos se usan para enviar datos a los módulos esclavos con diferentes direcciones, y el quinto grupo es se usa para enviar direcciones para leer los datos recibidos por el último módulo esclavo, así que solo envíe mosi según sea necesario;
- El esclavo del módulo esclavo juzgará los datos leídos mosi, si lee 1, juzgue si los siguientes 7 bits son la dirección, si es así, envíe los datos del módulo esclavo esclavo a la dirección a través de miso; si lee 0, juzgue el 7 bits siguientes Si el bit es la dirección, si lo es, los datos enviados por mosi se recibirán en el registro del módulo esclavo esclavo en la dirección.
3. código fuente FPGA
1.SPI_master: módulo principal
module spi_master(
input clk_40k,
input rst_n,
input [7:0] data_in,
input send_start,
output reg [7:0] data_out,
output reg data_out_vld,
output reg cs_n,
output reg sclk,
input miso,
output reg mosi
);
reg flag; //recieve&transfer part
reg addr_flag; //mosi address output
reg data_flag; //mosi data output
reg [4:0] bit_cnt; //bit count
reg [6:0] clk_cnt; //40 count
reg [2:0] cs_n_cnt; //data transform count
reg [7:0] data_in_m0; //shifting input data
reg [7:0] data_in_m1; //shifting input data
reg [7:0] data_in_m2; //shifting input data
reg [7:0] data_in_m3; //shifting input data
reg [7:0] rx_data_out; //output data
reg [7:0] addr0;
reg [7:0] addr1;
reg [7:0] addr2;
reg [7:0] addr3;
reg [7:0] read;
parameter reg0_address = 7'b0000000; //address of reg0
parameter reg1_address = 7'b0000001; //address of reg1
parameter reg2_address = 7'b0000010; //address of reg2
parameter reg3_address = 7'b0000011; //address of reg3
localparam idle = 1'b0;
localparam work = 1'b1;
localparam transfer = 1'b0;
localparam recieve = 1'b1;
//set address of reg
always @ *
begin
if(~rst_n)
begin
addr0 <= {
1'b0, reg0_address};
addr1 <= {
1'b0, reg1_address};
addr2 <= {
1'b0, reg2_address};
addr3 <= {
1'b0, reg3_address};
read <= {
1'b1, reg3_address};
end
end
//cs_n
always @ (posedge clk_40k or negedge rst_n)
begin
if(~rst_n)
cs_n <= 1'b1;
else if(send_start == 1'b1)
cs_n <= 1'b0;
else if(clk_cnt == 7'd39 && bit_cnt == 5'd15 && cs_n_cnt == 3'b100)
cs_n <= 1'b1;
end
//clk_cnt 20 count
always @ (posedge clk_40k or negedge rst_n)
begin
if(~rst_n)
clk_cnt <= 7'b0;
else if(cs_n == 1'b0 && clk_cnt != 7'd39)
clk_cnt <= clk_cnt + 1'b1;
else if(cs_n == 1'b0 && clk_cnt == 7'd39)
clk_cnt <= 7'b0;
end
//sclk
always @ (posedge clk_40k or negedge rst_n)
begin
if(~rst_n)
sclk <= 1'b0;
else if(send_start == 1'b1)
sclk <= 1'b1;
else if(clk_cnt == 7'd19 || clk_cnt == 7'd39)
sclk <= ~sclk;
end
//bit_cnt 16 count
always @ (posedge clk_40k or negedge rst_n)
begin
if(~rst_n || cs_n)
bit_cnt <= 5'b0;
else if(bit_cnt != 5'd15 && cs_n == 1'b0 && clk_cnt == 7'd39)
bit_cnt <= bit_cnt + 1'b1;
else if(bit_cnt == 5'd15 && cs_n == 1'b0 && clk_cnt == 7'd39)
bit_cnt <= 5'b0;
else if(cs_n == 1'b1)
bit_cnt <= 5'b0;
end
//cs_n_cnt 4 salve count
always @ (posedge clk_40k or negedge rst_n)
begin
if(~rst_n || cs_n)
cs_n_cnt <= 3'b0;
else if(bit_cnt == 5'd15 && cs_n_cnt != 3'b100 && clk_cnt == 7'd39)
cs_n_cnt <= cs_n_cnt + 1'b1;
else if(bit_cnt == 5'd15 && cs_n_cnt == 3'b100 && clk_cnt == 7'd39)
cs_n_cnt <= 3'b0;
else if(cs_n == 1'b1)
cs_n_cnt <= 3'b0;
end
//input data shifting
always @ (posedge sclk)
begin
if(send_start == 1'b1)
begin
data_in_m0 <= data_in;
data_in_m1 <= {
data_in[1:0],data_in[7:2]};
data_in_m2 <= {
data_in[3:0],data_in[7:4]};
data_in_m3 <= {
data_in[5:0],data_in[7:6]};
end
end
//send part mosi
//flag of transfering address and data
always @ (negedge sclk or negedge rst_n)
begin
if(~rst_n)
begin
addr_flag <= work;
data_flag <= idle;
end
else if(bit_cnt == 5'd7)
begin
addr_flag <= idle;
data_flag <= work;
end
else if(bit_cnt == 5'd15 || send_start == 1'b1)
begin
addr_flag <= work;
data_flag <= idle;
end
end
//output mosi address and data
always @ (posedge sclk)
begin
if(addr_flag == work && cs_n == 1'b0)
begin
case(cs_n_cnt)
3'b000:
begin
mosi <= addr0[7];
addr0 <= {
addr0[6:0],addr0[7]};
end
3'b001:
begin
mosi <= addr1[7];
addr1 <= {
addr1[6:0],addr1[7]};
end
3'b010:
begin
mosi <= addr2[7];
addr2 <= {
addr2[6:0],addr2[7]};
end
3'b011:
begin
mosi <= addr3[7];
addr3 <= {
addr3[6:0],addr3[7]};
end
3'b100:
begin
mosi <= read[7];
read <= {
read[6:0],read[7]};
end
endcase
end
else if(data_flag == work && cs_n == 1'b0)
begin
case(cs_n_cnt)
3'b000:
begin
mosi <= data_in_m0[7];
data_in_m0 <= {
data_in_m0[6:0],data_in_m0[7]};
end
3'b001:
begin
mosi <= data_in_m1[7];
data_in_m1 <= {
data_in_m1[6:0],data_in_m1[7]};
end
3'b010:
begin
mosi <= data_in_m2[7];
data_in_m2 <= {
data_in_m2[6:0],data_in_m2[7]};
end
3'b011:
begin
mosi <= data_in_m3[7];
data_in_m3 <= {
data_in_m3[6:0],data_in_m3[7]};
end
3'b100:
begin
mosi <= 1'bz;
end
endcase
end
else if(cs_n == 1'b1)
mosi <= 1'bz;
end
//recieve part miso
//recieve data
always @ (negedge sclk or negedge rst_n)
begin
if(~rst_n)
rx_data_out <= 8'b0;
else if(flag == 1'b1)
rx_data_out <= {
rx_data_out[6:0],miso};
end
//output & valid
always @ (posedge clk_40k or negedge rst_n)
begin
if(~rst_n)
data_out_vld <= 1'b0;
else if(bit_cnt == 5'd15 && clk_cnt== 7'd39 && cs_n == 1'b0 && cs_n_cnt == 3'b100)
begin
data_out_vld <= 1'b1;
data_out <= rx_data_out;
end
else
data_out_vld <= 1'b0;
end
//recieve/transfer switch
always @ (posedge clk_40k or negedge rst_n)
begin
if(~rst_n)
flag <= 1'b0;
else if(clk_cnt == 7'd39 && bit_cnt == 5'd7 && cs_n_cnt == 3'b100 && flag == 1'b0)
flag <= 1'b1;
else if(clk_cnt == 7'd0 && bit_cnt == 5'd0 && flag == 1'b1)
flag <= 1'b0;
end
endmodule
2.SPI_slave: módulo esclavo
module spi_slave
(
input rst_n,
input cs_n,
input sclk,
input mosi,
output reg miso,
output reg [7:0] reg0_out,
output reg [7:0] reg1_out,
output reg [7:0] reg2_out,
output reg [7:0] reg3_out
);
reg [6:0] bit_cnt;
reg state;
reg n_state;
reg [6:0] address;
reg token;
localparam idle = 1'b0;
localparam transmit = 1'b1;
//bit_cnt
always @ (posedge sclk or negedge rst_n)
if(~rst_n || cs_n)
bit_cnt <= 7'd0;
else if(bit_cnt == 7'd15)
bit_cnt <= 7'd0;
else if(state || n_state)
bit_cnt <= bit_cnt + 1'b1;
//n_state
always @ *
if(cs_n)
n_state <= idle;
else
n_state <= transmit;
//state
always @ (posedge sclk or negedge rst_n)
if(~rst_n)
state <= idle;
else
state <= n_state;
//reg_out
always @ (negedge sclk or negedge rst_n)
if(~rst_n)
begin
reg0_out <= 8'd0;
reg1_out <= 8'd0;
reg2_out <= 8'd0;
reg3_out <= 8'd0;
end
else if(bit_cnt >= 'd8 && token == 'b0)
begin
case(address)
7'd0:
reg0_out <= {
reg0_out[6:0], mosi};
7'd1:
reg1_out <= {
reg1_out[6:0], mosi};
7'd2:
reg2_out <= {
reg2_out[6:0], mosi};
7'd3:
reg3_out <= {
reg3_out[6:0], mosi};
endcase
end
//address
always @ (negedge sclk or negedge rst_n)
if(~rst_n)
address <= 7'd0;
else if(bit_cnt >= 7'd1 && bit_cnt <= 7'd7)
address <= {
address[5:0], mosi};
//token
always @ (negedge sclk or negedge rst_n)
if(~rst_n)
token <= 1'b0;
else if(state == transmit && bit_cnt == 7'b0)
token <= mosi;
//miso
always @ (posedge sclk or negedge rst_n)
if(~rst_n || state == idle || cs_n)
miso <= 1'b0;
else if(state == transmit && token == 1'b1 && bit_cnt >= 7'd7)
begin
case(address)
7'd0: miso <= reg0_out[14-bit_cnt];
7'd1: miso <= reg1_out[14-bit_cnt];
7'd2: miso <= reg2_out[14-bit_cnt];
7'd3: miso <= reg3_out[14-bit_cnt];
endcase
end
else
miso <= 1'b0;
endmodule
3.testbench: archivo de prueba
`timescale 1us/1us
module tb();
reg clk_40k;
reg rst_n;
reg [7:0] data_in;
reg send_start;
wire sclk;
wire cs_n;
wire mosi;
wire miso;
wire [7:0] data_out;
wire data_out_vld;
wire [7:0] reg0_out;
wire [7:0] reg1_out;
wire [7:0] reg2_out;
wire [7:0] reg3_out;
spi_master i_spi_master(
.clk_40k (clk_40k ),
.rst_n (rst_n ),
.data_in (data_in ),
.send_start (send_start),
.sclk (sclk ),
.cs_n (cs_n ),
.mosi (mosi ),
.miso (miso ),
.data_out (data_out ),
.data_out_vld (data_out_vld )
);
spi_slave i_spi_slave(
.rst_n (rst_n ),
.cs_n (cs_n ),
.sclk (sclk ),
.mosi (mosi ),
.miso (miso ),
.reg0_out (reg0_out ),
.reg1_out (reg1_out ),
.reg2_out (reg2_out ),
.reg3_out (reg3_out )
);
initial
begin
rst_n = 1'b0;
#10 rst_n = 1'b1;
end
initial
begin
clk_40k = 1'b0;
forever
#1 clk_40k = ~clk_40k;
end
initial
begin
send_start = 1'b0;
data_in = 8'd0;
forever
begin
#200;
data_in = $random()%256;
send_start = 1'b1;
#2
send_start = 1'b0;
#8000;
end
end
endmodule