diff --git a/.gitignore b/.gitignore index 48df0b8c80da778cde0640f4b28a412957a120ab..9be58d85e91178b4cfba7b73ea368b04cd11d568 100644 --- a/.gitignore +++ b/.gitignore @@ -22,5 +22,11 @@ asic/TSMC28nm/*.syn asic/TSMC28nm/command.log asic/TSMC28nm/default.svf asic/TSMC28nm/*.log +asic/TSMC28nm/alib-52 +docs/tex/*.aux +docs/tex/*.log +docs/tex/*.out +docs/tex/*.toc + imp \ No newline at end of file diff --git a/README.md b/README.md index 18bf7e7e0ad267f3f8f40bc28a533fd6b2281a02..1d4d7b039b30db4d61875aa111fc1834f732b8ae 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# AHB QSPI with XiP +# AHB QSPI for XiP Basic QSPI controller with AHB and APB interfaces, uses Arm Corelink AHB Flash Cache controller for the XiP Cache. AHB from cache controller and APB interface are mux'd to QSPI controller so access can to external memory can be from either. ## Prerequisites @@ -38,6 +38,7 @@ Read bandwidth with 4 KB cache for 512x 32 bit reads, and system clock of 200 MH - Add programmable op codes for AHB interface - Improve writing method over APB? (see below) - Add interrupts for QSPI finish +- Add programmable address length ## Writing over APB Currently the method for writing over APB to the flash is to write up to 4x 32 bit words to the APB registers and then tell the QSPI controller to write. However as there is a long latency after writing over QSPI for the flash to finish, this is not optimal. @@ -47,3 +48,10 @@ The flash device we are targeting is a SST26VF064B device which allows up to 256 1. Just simply add an extra 240 bytes of registers to the APB controller. This is the simplest to do but I think that this will inflate the overall area of the IP by too much, however this makes the software side fairly simple. (i.e. write 256 bytes to APB-> send command to QSPI controller-> wait for QSPI transaction to finish-> poll QSPI status until write is complete) 2. Add write fifo. This will be a lot smaller in terms of area. But will have to keep track of how many bytes get written over QSPI to limit to 256. This requires a bit of an overhaul in how the write transaction works but it means we can fully utilise the page program command. Software then becomes a little more complicated (i.e. write N bytes to APB-> send command to QSPI controller-> check if FIFO not full and send more data-> repeat last step 256/N times-> QSPI checks if FIFO is empty whilst the happens and stops transaction when empty-> poll QSPI status until write is complete). +## Project status +Behaviourial this IP is 'mostly' verified. The verification has been performed against a model of the SST26VF064B flash memory. I have so far verified the most important QSPI commands and the usage of the AHB interface to the QSPI controller. However further work should be performed to verify this IP against other memory models if possible. Also some extension to the IP should be made to support different flash sizes (i.e. change the number of address bits) as well as some optimisations for writing data (see above) + +Gate level simulations seem to match the behavioural. I do get some timing violations if I use the Arm standard cell timings + +FPGA implementation is ongoing, I would like to first test against the same flash model that I have used for behavioural verification, but I currently don't have this. The only chip I have utilises DDR which I don't want to support at this stage of the project. + diff --git a/asic/TSMC28nm/ahb_qspi_constraints.sdc b/asic/TSMC28nm/ahb_qspi_constraints.sdc index a0f491c9e67eee4ee4741ff5b63b022a1d78cc14..a391357be54e76fe905397de8f83f813f1e0a383 100644 --- a/asic/TSMC28nm/ahb_qspi_constraints.sdc +++ b/asic/TSMC28nm/ahb_qspi_constraints.sdc @@ -8,4 +8,12 @@ set PCLK_PERIOD 10 create_clock -name "$HCLK" -period $HCLK_PERIOD [get_ports HCLK] create_clock -name "$PCLK" -period $PCLK_PERIOD [get_ports PCLK] create_generated_clock -name "QSPI_SCLK" -source [get_ports HCLK] -divide_by 2 [get_pins u_qspi_controller/u_qspi_clock_div/QSPI_SCLK_i] +create_generated_clock -name "QSPI_SCLK_o" -source [get_pins u_qspi_controller/u_qspi_clock_div/QSPI_SCLK_i] -divide_by 1 [get_ports QSPI_SCLK] + + set_max_fanout 10 [all_inputs] + +set_input_delay 1 -clock "QSPI_SCLK_o" [get_ports QSPI_IO_i[*]] +set_output_delay 1 -clock "QSPI_SCLK_o" [get_ports QSPI_IO_e[*]] +set_output_delay 1 -clock "QSPI_SCLK_o" [get_ports QSPI_IO_o[*]] +set_output_delay 1 -clock "QSPI_SCLK_o" [get_ports QSPI_nCS] \ No newline at end of file diff --git a/docs/ahb_qspi.pdf b/docs/ahb_qspi.pdf new file mode 100644 index 0000000000000000000000000000000000000000..0bf6f64a496bc993d6bd8e7f1694b2a3cf4cd9bd Binary files /dev/null and b/docs/ahb_qspi.pdf differ diff --git a/docs/tex/ahb_qspi.tex b/docs/tex/ahb_qspi.tex new file mode 100644 index 0000000000000000000000000000000000000000..aa5efbe7d10a1725224c1eb809f59b2b002b2013 --- /dev/null +++ b/docs/tex/ahb_qspi.tex @@ -0,0 +1,208 @@ +\documentclass{report} +\usepackage{hyperref} +\usepackage{tcolorbox,float} +\usepackage{listings} + +\makeatletter +\newfloat{info@box}{tbp}{loi}[section]% 1: Name of float environment. 2: Default placement (top, bottom, ...). 3: File extension if written to an aux-file (like toc, lof, lot, loa, ...). 4: Numbering within <section/subsection/...>. +\makeatother +\floatname{info@box}{Infobox}% Adapt caption. +\newenvironment{infobox}[1][]{% Create new environment using info@box and tcolorbox + \begin{info@box}% + \begin{tcolorbox}[colback=red!15!white,% background color + colframe=red!75!black,% frame color + title=Additional information\ifstrempty{#1}{}{: #1}.% title + ]% +}{% + \end{tcolorbox}% + \end{info@box}% +} + +\title{SoCLabs XiP AHB QSPI Configuration and User Manual} +\author{Daniel Newbrook, \href{http://www.soclabs.org}{SoC Labs}} +\begin{document} +\maketitle +\tableofcontents +\clearpage +\chapter{Introduction} +In SoC designs it is often quite valuable to have external non-volatile memory. Typically this will be +where instruction code can be stored, and ideally we don't want to have to program this memory space after +every power cycle. + +QSPI flash is fairly inexpensive and the protocol is fairly simple to implement. Whilst it isn't the fastest +protocol that you could use, it also doesn't require any real effort in the analog domain unlike for example SATA. + +Excecute in place (XiP) is the method of making an external flash look like a memory mapped region to the processor. +Meaning the processor can directly run instructions from this address space. We add a small cache between the +QSPI controller and the bus to reduce access time for repeat accesses. + +The design here uses an AHB interface for the main XiP interface, and an APB interface for access to internal +registers of this IP, as well as to send commands over the QSPI interface. + +\section{IP} +The SoCLabs AHB QSPI relies on IP's from certain vendors. In order to build you will need the following IP. +\subsection{Arm(R)} +Arm IP should be downloaded from Arm and placed into the recommended IP directories. +\begin{itemize} + \item Corstone-101 + \item CG092 (flash cache) +\end{itemize} + +\section{Integrating this IP} +There are 2 steps to integrating this IP in an SoC +\begin{enumerate} + \item Include the IP in the file list + \item Instantiate the IP in the design +\end{enumerate} + +For step 1. you make an envionment variable at your top level project pointing to the ahb qspi directory called +SOCLABS\_AHB\_QSPI\_DIR (this will help with path references). You should then include the ahb\_QSPI\_SIM.flist to your +behavioural flist, and ahb\_QSPI\_ASIC.flist to your asic flist. + +For step 2. you need an APB and AHB port to connect to, and then to bring out the QSPI wires to the top of your design. +I think connecting these to simple tristate pads should be fine. + +\chapter{Programmers Manual} + +\section{Address Map} + +The APB interface can access a number of registers. Outlined below are only the ones relating to the developed IP here, +for the CG092 register please see documentation from Arm. + +\begin{table}[!h] + \begin{center} + \caption{Register Address Map} + \label{address-map} + \begin{tabular}{||c | c ||} + \hline + Register & Address \\ + \hline\hline + Control Reg & 0x0000 \\ + \hline + Status Reg & 0x0004 \\ + \hline + SPI Commands & 0x0008 \\ + \hline + SPI Address & 0x000C \\ + \hline + Read Data 0 & 0x0010 \\ + \hline + Read Data 1 & 0x0014 \\ + \hline + Read Data 2 & 0x0018 \\ + \hline + Read Data 3 & 0x001C \\ + \hline + Write Data 0 & 0x0020 \\ + \hline + Write Data 1 & 0x0024 \\ + \hline + Write Data 2 & 0x0028 \\ + \hline + Write Data 3 & 0x002C \\ + \hline + CG092 Cache control & 0x1000-0x1FFF \\ + \hline + \end{tabular} +\end{center} +\end{table} + +\begin{table}[!h] + \begin{center} + \caption{Control Register Bits} + \label{address-map} + \begin{tabular}{||c | c ||} + \hline + Bits & Function \\ + \hline\hline + 0 & QSPI Quad IO Mode \\ + \hline + 8 & XiP Active \\ + \hline + 16-23 & QSPI Mode Code \\ + \hline + 24 & Continous Read Active \\ + \hline + 25 & No CMD active \\ + \hline + \end{tabular} +\end{center} +\end{table} + +\begin{table}[!h] + \begin{center} + \caption{Status Register Bits} + \label{address-map} + \begin{tabular}{||c | c ||} + \hline + Bits & Function \\ + \hline\hline + 0 & QSPI Busy \\ + \hline + \end{tabular} +\end{center} +\end{table} + +\begin{table}[!h] + \begin{center} + \caption{SPI Commands Register Bits} + \label{address-map} + \begin{tabular}{||c | c ||} + \hline + Bits & Function \\ + \hline\hline + 0-7 & QSPI Command \\ + \hline + 8 & QSPI Enable \\ + \hline + 9 & QSPI Read Enable \\ + \hline + 10 & QSPI Write Enable \\ + \hline + 11 & QSPI Address enable \\ + \hline + 12-15 & Number Dummy cycles (N-1) \\ + \hline + 16-19 & Number R/W Bytes (N-1) \\ + \hline + \end{tabular} +\end{center} +\end{table} + +\begin{table}[!h] + \begin{center} + \caption{QSPI Address Register Bits} + \label{address-map} + \begin{tabular}{||c | c ||} + \hline + Bits & Function \\ + \hline\hline + 0-21 & QSPI Address \\ + \hline + \end{tabular} +\end{center} +\end{table} + +\section{Setting XiP Mode} +Typical flow for entering XiP +\begin{enumerate} + \item Reset the QSPI flash + \item Enable QSPI Mode + \item Set Continous Read mode + \item Set XiP/AHB Mode + \item Configure Cache +\end{enumerate} + +\section{Write to Flash} +Typical flow for writing to flash +\begin{enumerate} + \item Reset the QSPI Flash + \item Enable QSPI mode + \item Clear write protection + \item Set write enable + \item Write to APB registers + \item Write to flash + \item Poll flash status until complete +\end{enumerate} + +\end{document} \ No newline at end of file diff --git a/logical/ahb_qspi_interface/logical/ahb_qspi_interface.sv b/logical/ahb_qspi_interface/logical/ahb_qspi_interface.sv index 22237ed98a7755bcad18cc5501a42d47bbb9f9aa..895c884f606589b2e75def53952cf80c19deea9f 100644 --- a/logical/ahb_qspi_interface/logical/ahb_qspi_interface.sv +++ b/logical/ahb_qspi_interface/logical/ahb_qspi_interface.sv @@ -1,7 +1,7 @@ module ahb_qspi_interface #( parameter ADDR_W = 32, - parameter DATA_W = 32 + parameter DATA_W = 32 )( input wire HCLK, input wire HRESETn, @@ -64,14 +64,14 @@ reg last_HWRITE; reg [1:0] last_HTRANS; reg [2:0] last_HSIZE; -// +// reg qspi_ready; reg qspi_started; assign HREADYOUT = (current_state==IDLE && last_HTRANS==2'b00) ? 1'b1: qspi_ready; //(current_state==IDLE)? 1'b1 : qspi_ready; -always @(posedge HCLK or negedge HRESETn) begin - if(~HRESETn) begin +always @(posedge HCLK or negedge HRESETn) begin + if(~HRESETn) begin last_HSEL <= 1'b0; last_HADDR <= {ADDR_W{1'b0}}; last_HWRITE <= 1'b0; @@ -79,7 +79,7 @@ always @(posedge HCLK or negedge HRESETn) begin last_HSIZE <= 3'h0; current_state <= IDLE; - end else begin + end else begin last_HSEL <= HSELx; last_HADDR <= HADDR; last_HWRITE <= HWRITE; @@ -90,7 +90,7 @@ always @(posedge HCLK or negedge HRESETn) begin end end -always @(*) begin +always_comb begin next_state = IDLE; case(current_state) @@ -104,26 +104,27 @@ always @(*) begin end WAIT_WRITE: if(qspi_ready) next_state = WRITE; - else + else next_state = WAIT_WRITE; WAIT_READ: if(qspi_ready) next_state = READ; - else + else next_state = WAIT_READ; READ: next_state = IDLE; WRITE: next_state = IDLE; + default: next_state = IDLE; endcase end -always @(posedge HCLK or negedge HRESETn) begin +always @(posedge HCLK or negedge HRESETn) begin if(~HRESETn) begin AHB_QSPI_ENABLE <= 1'b0; qspi_ready <= 1'b0; qspi_started <= 1'b0; AHB_QSPI_ADDR <= 22'd0; end else begin - if(current_state == WAIT_READ) begin - if(AHB_QSPI_BUSY!=1'b1) begin + if(current_state == WAIT_READ) begin + if(AHB_QSPI_BUSY!=1'b1) begin if(qspi_started==1'b0) begin AHB_QSPI_ENABLE <= 1'b1; qspi_started <= 1'b1; @@ -131,7 +132,7 @@ always @(posedge HCLK or negedge HRESETn) begin end if(AHB_QSPI_ENABLE_ACK) AHB_QSPI_ENABLE <= 1'b0; - if(qspi_started==1'b1) begin + if(qspi_started==1'b1) begin if(AHB_QSPI_BUSY==1'b0) begin HRDATA <= AHB_QSPI_RDATA_i; qspi_ready <= 1'b1; @@ -146,4 +147,4 @@ always @(posedge HCLK or negedge HRESETn) begin end -endmodule \ No newline at end of file +endmodule diff --git a/logical/apb_qspi_regs/logical/apb_qspi_regs.v b/logical/apb_qspi_regs/logical/apb_qspi_regs.v index 458e8c38110582e6966fb13cee54a7cc5ecf30cc..edb6383c6d0dd56914391bcef14cc90cc128996c 100644 --- a/logical/apb_qspi_regs/logical/apb_qspi_regs.v +++ b/logical/apb_qspi_regs/logical/apb_qspi_regs.v @@ -31,21 +31,21 @@ module apb_qspi_regs( output wire QSPI_CONT_READ, output wire [7:0] QSPI_MODE_CODE, output wire QSPI_NO_CMD -); +); // Registers // reg0 Control 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 // QSPI_QIO_MODE[0] | | | | | |--> QSPI_QIO_MODE [0] // XIP_ACTIVE [8] | | | | |------------------> XIP_ACTIVE [8] -// QSPI_MODE_CODE[23:16] | | |<------------------>|----------------------------------------> QSPI_MODE_CODE[23:16] -// QSPI_CONT_READ[24] | |----------------------------------------------------------------> QSPI_CONT_READ[24] +// QSPI_MODE_CODE[23:16] | | |<------------------>|----------------------------------------> QSPI_MODE_CODE[23:16] +// QSPI_CONT_READ[24] | |----------------------------------------------------------------> QSPI_CONT_READ[24] // QSPI_NO_CMD[25] |-------------------------------------------------------------------> QSPI_NO_CMD[25] // reg1 Status // reg2 SPI Commands 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 // QSPI_CMD [7:0] | | | | | | | | | |----> QSPI_CMD [7:0] -// QSPI_ENABLE [8] | | | | | | | | |---> QSPI_ENABLE [8] -// QSPI_READ [9] | | | | | | | |-------> QSPI_READ [9] +// QSPI_ENABLE [8] | | | | | | | | |---> QSPI_ENABLE [8] +// QSPI_READ [9] | | | | | | | |-------> QSPI_READ [9] // QSPI_WRITE [10] | | | | | | |--------> QSPI_WRITE [10] // QSPI_ADDR_EN[11] | | | | | |---------> QSPI_ADDR_EN [11] // QSPI_DUMMY_CYCLES[15:12] | | | |<------>|-------> QSPI_DUMMY_CYCLES [15:12] @@ -110,7 +110,7 @@ always @(posedge PCLK or negedge PRESETn) begin 10'h00A: reg10 = PWDATA; 10'h00B: reg11 = PWDATA; endcase - end + end if(QSPI_ENABLE_ACK) reg2[8]=1'b0; reg1[0] = QSPI_BUSY; @@ -118,8 +118,8 @@ always @(posedge PCLK or negedge PRESETn) begin end always @(PCLK, PADDR) begin - if(PCLK) begin - case(PADDR) + if(PCLK) begin + case(PADDR) 10'h000: PRDATA = reg0; 10'h001: PRDATA = reg1; 10'h002: PRDATA = reg2; @@ -140,4 +140,4 @@ end assign PREADY = 1'b1; //(PSEL & PENABLE & PWRITE) | (PSEL & ~PWRITE); assign PSLVERR = 1'b0; -endmodule \ No newline at end of file +endmodule diff --git a/logical/cache_models/generic/cache_ram.v b/logical/cache_models/generic/cache_ram.v index 25756b9f9e564a5306eecce2c2258c1113c5d791..7872c17b6467d1f220bb44c361f488b1fb5b52bd 100644 --- a/logical/cache_models/generic/cache_ram.v +++ b/logical/cache_models/generic/cache_ram.v @@ -23,7 +23,9 @@ module cache_ram #( wire cs_ram0_word0; assign cs_ram0_word0 = CLD_CS[0] & (CLD_WE | CLD_RD[0]); -p_flash_cache_f0_sp_ram #(.DATA_WIDTH(32), .MEMORY_DEPTH(2**(ADDR_W)), .ADDR_WIDTH(ADDR_W)) data_ram_0_word_0_i ( +p_flash_cache_f0_sp_ram #(.DATA_WIDTH(32), + .MEMORY_DEPTH(2**(ADDR_W)), + .ADDR_WIDTH(ADDR_W)) data_ram_0_word_0_i ( .CLK (CLK), .A (CLD_ADDR), .CEN (~cs_ram0_word0), @@ -38,7 +40,9 @@ p_flash_cache_f0_sp_ram #(.DATA_WIDTH(32), .MEMORY_DEPTH(2**(ADDR_W)), .ADDR_WID wire cs_ram0_word1; assign cs_ram0_word1 = CLD_CS[1] & (CLD_WE | CLD_RD[1]); -p_flash_cache_f0_sp_ram #(.DATA_WIDTH(32), .MEMORY_DEPTH(2**(ADDR_W)), .ADDR_WIDTH(ADDR_W)) data_ram_0_word_1_i ( +p_flash_cache_f0_sp_ram #(.DATA_WIDTH(32), + .MEMORY_DEPTH(2**(ADDR_W)), + .ADDR_WIDTH(ADDR_W)) data_ram_0_word_1_i ( .CLK (CLK), .A (CLD_ADDR), .CEN (~cs_ram0_word1), @@ -53,7 +57,9 @@ p_flash_cache_f0_sp_ram #(.DATA_WIDTH(32), .MEMORY_DEPTH(2**(ADDR_W)), .ADDR_WID wire cs_ram0_word2; assign cs_ram0_word2 = CLD_CS[2] & (CLD_WE | CLD_RD[2]); -p_flash_cache_f0_sp_ram #(.DATA_WIDTH(32), .MEMORY_DEPTH(2**(ADDR_W)), .ADDR_WIDTH(ADDR_W)) data_ram_0_word_2_i ( +p_flash_cache_f0_sp_ram #(.DATA_WIDTH(32), + .MEMORY_DEPTH(2**(ADDR_W)), + .ADDR_WIDTH(ADDR_W)) data_ram_0_word_2_i ( .CLK (CLK), .A (CLD_ADDR), .CEN (~cs_ram0_word2), @@ -68,7 +74,9 @@ p_flash_cache_f0_sp_ram #(.DATA_WIDTH(32), .MEMORY_DEPTH(2**(ADDR_W)), .ADDR_WID wire cs_ram0_word3; assign cs_ram0_word3 = CLD_CS[3] & (CLD_WE | CLD_RD[3]); -p_flash_cache_f0_sp_ram #(.DATA_WIDTH(32), .MEMORY_DEPTH(2**(ADDR_W)), .ADDR_WIDTH(ADDR_W)) data_ram_0_word_3_i ( +p_flash_cache_f0_sp_ram #(.DATA_WIDTH(32), + .MEMORY_DEPTH(2**(ADDR_W)), + .ADDR_WIDTH(ADDR_W)) data_ram_0_word_3_i ( .CLK (CLK), .A (CLD_ADDR), .CEN (~cs_ram0_word3), @@ -83,7 +91,9 @@ p_flash_cache_f0_sp_ram #(.DATA_WIDTH(32), .MEMORY_DEPTH(2**(ADDR_W)), .ADDR_WID wire cs_tag0; assign cs_tag0 = TAG_CS & (TAG_WE | TAG_RD); -p_flash_cache_f0_sp_ram #(.DATA_WIDTH(11), .MEMORY_DEPTH(2**(ADDR_W)), .ADDR_WIDTH(ADDR_W)) tag_ram_0_i ( +p_flash_cache_f0_sp_ram #(.DATA_WIDTH(11), + .MEMORY_DEPTH(2**(ADDR_W)), + .ADDR_WIDTH(ADDR_W)) tag_ram_0_i ( .CLK (CLK), .A (TAG_ADDR), .CEN (~cs_tag0), @@ -94,4 +104,4 @@ p_flash_cache_f0_sp_ram #(.DATA_WIDTH(11), .MEMORY_DEPTH(2**(ADDR_W)), .ADDR_WID -endmodule \ No newline at end of file +endmodule diff --git a/logical/cache_subsytem/logical/cache_subsystem.v b/logical/cache_subsytem/logical/cache_subsystem.v index 118857cf1be46498cde687d3360f1359a1b4161f..4f56389a1717562a1932f3412f30e89dbd02dca1 100644 --- a/logical/cache_subsytem/logical/cache_subsystem.v +++ b/logical/cache_subsytem/logical/cache_subsystem.v @@ -2,8 +2,8 @@ module cache_subsystem( input wire HCLK, input wire HRESETn, - // APB - input wire PCLKG, + // APB + input wire PCLKG, input wire [11:0] PADDR, input wire [31:0] PWDATA, input wire PWRITE, @@ -29,9 +29,9 @@ module cache_subsystem( input wire HMASTERS, // When 1 indicate a debug access output wire HREADYOUTS, // Device ready output wire [31:0] HRDATAS, // Read data output - output wire HRESPS, - - // AHB Manager + output wire HRESPS, + + // AHB Manager output wire HSELM, // Device select output wire [21:0] HADDRM, // Address output wire [2:0] HBURSTM, // Burst Type - Not used (tied to 0)! @@ -173,10 +173,10 @@ p_flash_cache_f0 #( .CACHEHIT(CACHEHIT) ); -always @(posedge HCLK or negedge HRESETn) begin +always @(posedge HCLK or negedge HRESETn) begin if(~HRESETn) PWR_DELAY <= 1'b0; - else + else PWR_DELAY <= RAMPWRUPREQ; end assign RAMPWRUPACK = PWR_DELAY; @@ -220,4 +220,4 @@ cache_ram #( ); -endmodule \ No newline at end of file +endmodule diff --git a/logical/qspi_controller/logical/qspi_clock_div.v b/logical/qspi_controller/logical/qspi_clock_div.v index 40b2025e74af07cff8ed6c7abe6bc74d59b29409..7ece1792f4ff37f1d37e775f5dba83355cbd2b4e 100644 --- a/logical/qspi_controller/logical/qspi_clock_div.v +++ b/logical/qspi_controller/logical/qspi_clock_div.v @@ -15,17 +15,17 @@ always @(posedge HCLK or negedge HRESETn) begin if(~HRESETn) begin QSPI_CLK_DIV_COUNTER<=8'h00; QSPI_SCLK_reg <= 1'b0; - end else begin - if(QSPI_CLK_DIV==8'h01) begin + end else begin + if(QSPI_CLK_DIV==8'h01) begin QSPI_SCLK_reg <= ~QSPI_SCLK_reg; - end else if(QSPI_CLK_DIV!=8'h00) begin - if(QSPI_CLK_DIV_COUNTER==QSPI_CLK_DIV-1'b1) begin + end else if(QSPI_CLK_DIV!=8'h00) begin + if(QSPI_CLK_DIV_COUNTER==QSPI_CLK_DIV-1'b1) begin QSPI_CLK_DIV_COUNTER <= 8'h00; - end else begin + end else begin QSPI_SCLK_reg <= ~QSPI_SCLK_reg; QSPI_CLK_DIV_COUNTER <= QSPI_CLK_DIV_COUNTER + 1; end - end else begin + end else begin QSPI_SCLK_reg <= QSPI_SPI_MODE[1]; end end @@ -33,4 +33,4 @@ end -endmodule \ No newline at end of file +endmodule diff --git a/logical/qspi_controller/logical/qspi_controller.sv b/logical/qspi_controller/logical/qspi_controller.sv index 94c1127be53c8ea14fa05d6cd8cea211ca33fb7c..73525d118b11ff6b8d7dce878683cf5cd3f4c572 100644 --- a/logical/qspi_controller/logical/qspi_controller.sv +++ b/logical/qspi_controller/logical/qspi_controller.sv @@ -4,8 +4,8 @@ module qspi_controller ( input wire HRESETn, input wire [7:0] QSPI_CLK_DIV, - input wire [1:0] QSPI_SPI_MODE, // {CPOL, CPHA} - + input wire [1:0] QSPI_SPI_MODE, // {CPOL, CPHA} + input wire [7:0] QSPI_CMD, input wire QSPI_ENABLE, output reg QSPI_ENABLE_ACK, @@ -45,7 +45,8 @@ qspi_clock_div u_qspi_clock_div( assign QSPI_SCLK = (QSPI_SCLK_e==1'b1) ? QSPI_SCLK_i : QSPI_SPI_MODE[1]; -enum {IDLE, NO_FETCH, OP, ADDR, MODE, DUMMY, DATA_O, DATA_I} current_state, next_state; +typedef enum {IDLE, NO_FETCH, OP, ADDR, MODE, DUMMY, DATA_O, DATA_I} fsm_state_e; +fsm_state_e current_state, next_state; reg [2:0] op_counter; reg [4:0] addr_counter; @@ -65,14 +66,14 @@ reg [127:0] QSPI_WDATA_reg; assign QSPI_IO_o = QSPI_IO_o_reg; always @(posedge QSPI_SCLK_i or negedge HRESETn) begin - if(~HRESETn) begin + if(~HRESETn) begin current_state <= IDLE; - end else begin + end else begin current_state <= next_state; end -end +end -always @(*) begin +always_comb begin case(current_state) IDLE: begin if(QSPI_ENABLE==1'b1 && QSPI_NO_CMD==1'b0) begin @@ -85,10 +86,10 @@ always @(*) begin end else begin next_state = IDLE; QSPI_ENABLE_ACK = 1'b0; - end - //op_counter=3'h0; + end + //op_counter=3'h0; end - NO_FETCH: begin + NO_FETCH: begin QSPI_ENABLE_ACK = 1'b1; if(QSPI_ENABLE==1'b0) next_state = IDLE; @@ -121,10 +122,10 @@ always @(*) begin next_state = DATA_O; else next_state = IDLE; - end else begin + end else begin next_state = OP; end - end + end ADDR: begin QSPI_ENABLE_ACK = 1'b1; if(QSPI_QIO_MODE==1'b1 & addr_counter==5'h05) begin @@ -140,7 +141,7 @@ always @(*) begin next_state = DATA_O; else next_state = IDLE; - end + end else if(QSPI_QIO_MODE==1'b0 & addr_counter==5'h15) begin QSPI_ENABLE_ACK = 1'b0; if(QSPI_DUMMY_CYCLES!=4'h0) @@ -151,12 +152,12 @@ always @(*) begin next_state = DATA_O; else next_state = IDLE; - end else begin + end else begin next_state = ADDR; end - end - MODE: begin - if(QSPI_QIO_MODE==1'b1 & mode_counter == 1'b0) begin + end + MODE: begin + if(QSPI_QIO_MODE==1'b1 & mode_counter == 1'b0) begin if(QSPI_DUMMY_CYCLES!=4'h0) next_state = DUMMY; else if(QSPI_READ) @@ -165,7 +166,7 @@ always @(*) begin next_state = DATA_O; else next_state = IDLE; - end + end else if(QSPI_QIO_MODE==1'b0) begin next_state = IDLE; end @@ -181,57 +182,58 @@ always @(*) begin end else begin next_state = DUMMY; end - end + end DATA_O: begin - if(byte_counter==5'h1F) begin + if(byte_counter==5'h1F) begin next_state = DATA_O; end else begin - if(QSPI_QIO_MODE==1'b0) begin - if(byte_counter==QSPI_N_RW_BYTES) begin + if(QSPI_QIO_MODE==1'b0) begin + if(byte_counter==QSPI_N_RW_BYTES) begin if(data_counter == 3'h7) next_state = IDLE; - else + else next_state = DATA_O; - end else begin + end else begin next_state = DATA_O; - end - end else begin + end + end else begin if(byte_counter==QSPI_N_RW_BYTES) begin if(data_counter==3'h0) next_state = IDLE; else next_state = DATA_O; - end else begin + end else begin next_state = DATA_O; end end end - end - DATA_I: begin + end + DATA_I: begin next_state = DATA_I; - if(QSPI_QIO_MODE==1'b0) begin + if(QSPI_QIO_MODE==1'b0) begin if((byte_counter[3:0]==QSPI_N_RW_BYTES)) begin if(data_counter==3'h7) next_state = IDLE; - else + else next_state = DATA_I; end end else begin if((byte_counter==QSPI_N_RW_BYTES+1)) begin if(data_counter==3'h0) next_state = IDLE; - else + else next_state = DATA_I; end end end + default: next_state = IDLE; endcase end reg QSPI_IO_e_int; always @(negedge QSPI_SCLK_i) begin - if(current_state==OP) begin + if(current_state==OP) begin data_in_reg <= 128'd0; QSPI_IO_e_int <= 1'b1; QSPI_SCLK_e <= 1'b1; @@ -248,10 +250,10 @@ always @(negedge QSPI_SCLK_i) begin end else if(current_state == DATA_O) begin QSPI_IO_e_int <= 1'b1; QSPI_SCLK_e <= 1'b1; - if(QSPI_QIO_MODE==1'b1) begin + if(QSPI_QIO_MODE==1'b1) begin QSPI_IO_o_reg <= QSPI_WDATA_reg[3:0]; QSPI_WDATA_reg <= {4'h0, QSPI_WDATA_reg[127:4]}; - end else begin + end else begin QSPI_IO_o_reg[0] <= QSPI_WDATA_reg[0]; QSPI_WDATA_reg <= {1'b0, QSPI_WDATA_reg[127:1]}; end @@ -266,17 +268,17 @@ always @(negedge QSPI_SCLK_i) begin QSPI_IO_o_reg[3:0]<={3'h7, addr_code[23]}; addr_code[23:0] <= {addr_code[22:0], 1'b0}; end - end else if(current_state == DUMMY) begin + end else if(current_state == DUMMY) begin QSPI_IO_e_int <= 1'b1; QSPI_SCLK_e <= 1'b1; - end else if(current_state == MODE) begin + end else if(current_state == MODE) begin QSPI_IO_e_int <=1'b1; QSPI_SCLK_e <= 1'b1; - if(QSPI_QIO_MODE==1'b1) begin + if(QSPI_QIO_MODE==1'b1) begin QSPI_IO_o_reg <= qspi_mode_reg[7:4]; qspi_mode_reg <= {qspi_mode_reg[3:0], 4'b0}; end - end else begin + end else begin op_code <= QSPI_CMD; addr_code <= {2'b00,QSPI_ADDR}; qspi_mode_reg <= QSPI_MODE_CODE; @@ -285,73 +287,73 @@ always @(negedge QSPI_SCLK_i) begin QSPI_IO_e_int <= 1'b1; end if(current_state == DATA_I) begin - if(QSPI_QIO_MODE==1'b1) begin + if(QSPI_QIO_MODE==1'b1) begin if(byte_counter!=QSPI_N_RW_BYTES+1) data_in_reg <= {data_in_reg[123:0], QSPI_IO_i}; - end else begin + end else begin if(byte_counter!=QSPI_N_RW_BYTES+1) data_in_reg <= {data_in_reg[126:0], QSPI_IO_i[1]}; end - end - + end + - if(current_state != DATA_O) begin + if(current_state != DATA_O) begin QSPI_WDATA_reg <= QSPI_WDATA; end end -always @(posedge QSPI_SCLK_i or negedge HRESETn) begin - if(~HRESETn) begin +always @(posedge QSPI_SCLK_i or negedge HRESETn) begin + if(~HRESETn) begin op_counter <= 3'h0; data_counter <= 3'h7; byte_counter <= 5'h1F; dummy_counter <= 4'h0; addr_counter <= 5'h00; mode_counter <= 1'b1; - end else begin - if(current_state==OP) begin + end else begin + if(current_state==OP) begin op_counter<=op_counter+1; - end else begin + end else begin op_counter <= 3'h0; end if(current_state==DATA_I || current_state ==DATA_O) begin data_counter <= data_counter + 1; - if(QSPI_QIO_MODE==1'b0 && data_counter == 3'h7) begin + if(QSPI_QIO_MODE==1'b0 && data_counter == 3'h7) begin byte_counter <= byte_counter+1; data_counter <= 3'h0; end else if (QSPI_QIO_MODE==1'b1 && data_counter >= 3'h1) begin byte_counter <= byte_counter+1; data_counter <= 3'h0; end - end else begin + end else begin data_counter <= 3'h7; byte_counter <= 5'h1F; end - if(current_state == DUMMY) begin + if(current_state == DUMMY) begin dummy_counter <= dummy_counter + 1; - end else begin + end else begin dummy_counter <= 4'h0; end - if(current_state == ADDR) begin + if(current_state == ADDR) begin addr_counter <= addr_counter + 1; - end else begin + end else begin addr_counter <= 5'h00; end - if(current_state == MODE) begin + if(current_state == MODE) begin mode_counter <= mode_counter + 1; - end else begin + end else begin mode_counter <= 1'b1; end end end // RDATA latch -always @(*) begin - if(~HRESETn) begin +always_comb begin + if(~HRESETn) begin QSPI_RDATA = 128'd0; end else if(QSPI_nCS) begin case(QSPI_N_RW_BYTES) @@ -371,7 +373,8 @@ always @(*) begin 4'hD: QSPI_RDATA = {{16{1'b0}}, data_in_reg[31:0], data_in_reg[63:32], data_in_reg[95:64], data_in_reg[111:96]}; 4'hE: QSPI_RDATA = {{8{1'b0}}, data_in_reg[31:0], data_in_reg[63:32], data_in_reg[95:64], data_in_reg[119:96]}; 4'hF: QSPI_RDATA = {data_in_reg[31:0], data_in_reg[63:32], data_in_reg[95:64], data_in_reg[127:96]}; - endcase + default: QSPI_RDATA = 128'd0; + endcase end end @@ -382,4 +385,4 @@ assign QSPI_IO_e[3] = (QSPI_QIO_MODE==1'b1)?QSPI_IO_e_int:1'b1; assign QSPI_IO_e[2] = (QSPI_QIO_MODE==1'b1)?QSPI_IO_e_int:1'b1; assign QSPI_IO_e[1] = (QSPI_QIO_MODE==1'b1)?QSPI_IO_e_int:1'b0; assign QSPI_IO_e[0] = (QSPI_QIO_MODE==1'b1)?QSPI_IO_e_int:1'b1; -endmodule \ No newline at end of file +endmodule diff --git a/logical/qspi_controller/logical/qspi_controller_mux.v b/logical/qspi_controller/logical/qspi_controller_mux.v index 1c6ae10b041b44e6252d4d4bcfa7eba856898e0c..b4a15832338419e8b50517d26e47fc188a4f3ca8 100644 --- a/logical/qspi_controller/logical/qspi_controller_mux.v +++ b/logical/qspi_controller/logical/qspi_controller_mux.v @@ -65,4 +65,4 @@ assign APB_QSPI_ENABLE_ACK = (XIP_ACTIVE==1'b0) ? QSPI_ENABLE_ACK : 1'b0; assign APB_QSPI_BUSY = (XIP_ACTIVE==1'b0) ? QSPI_BUSY : 1'b1; assign APB_QSPI_RDATA = (XIP_ACTIVE==1'b0) ? QSPI_RDATA : 128'd0; -endmodule \ No newline at end of file +endmodule diff --git a/logical/top_ahb_qspi/logical/top_ahb_qspi.v b/logical/top_ahb_qspi/logical/top_ahb_qspi.v index 735d925309fa99ee00691eced0a2ad16eb982570..be3ea393e9bed9ce143118085e01e044f7519901 100644 --- a/logical/top_ahb_qspi/logical/top_ahb_qspi.v +++ b/logical/top_ahb_qspi/logical/top_ahb_qspi.v @@ -5,12 +5,12 @@ // Contributors // // Daniel Newbrook (d.newbrook@soton.ac.uk) -// -// Copyright � 2021-4, SoC Labs (www.soclabs.org) +// +// Copyright � 2021-5, SoC Labs (www.soclabs.org) //----------------------------------------------------------------------------- // Purpose: -// -// +// +// //----------------------------------------------------------------------------- // Modules instantiated: // cmsdk_apb_slave_mux (u_cmsdk_apb_slave_mux) @@ -52,7 +52,7 @@ module top_ahb_qspi #( input wire [3:0] PSTRB, output wire [31:0] PRDATA, output wire PREADY, - output wire PSLVERR, + output wire PSLVERR, // QSPI Signals output wire QSPI_SCLK, @@ -74,7 +74,7 @@ wire cache_pready; wire cache_pslverr; - // AHB Outputs +// AHB Outputs wire flash_HSELM; // Device select wire [21:0] flash_HADDRM; // Address wire [2:0] flash_HBURSTM; // Burst Type - Not used (tied to 0)! @@ -422,4 +422,4 @@ qspi_controller u_qspi_controller( .QSPI_IO_i(QSPI_IO_i), .QSPI_IO_e(QSPI_IO_e) ); -endmodule \ No newline at end of file +endmodule diff --git a/makefile b/makefile index 652c61ca88e0edcbf3528ed2e3101cf5a66955d1..a874931cbba3ba0ec21a9b9e0fd54991da29b608 100644 --- a/makefile +++ b/makefile @@ -23,6 +23,11 @@ else DESIGN_VC ?= $(SOCLABS_AHB_QSPI_DIR)/flist/Top/ahb_QSPI_SIM.flist endif +documentation: + pdflatex --output-directory=./docs/tex/ ./docs/tex/ahb_qspi.tex + pdflatex --output-directory=./docs/tex/ ./docs/tex/ahb_qspi.tex + mv ./docs/tex/ahb_qspi.pdf ./docs/ahb_qspi.pdf + get_flash_model: @cd $(SOCLABS_AHB_QSPI_DIR)/verif/VIP; wget https://ww1.microchip.com/downloads/en/DeviceDoc/SST26VF064B.zip @cd $(SOCLABS_AHB_QSPI_DIR)/verif/VIP; unzip SST26VF064B.zip diff --git a/verif/cocotb/ahb_qspi_cocotb.v b/verif/cocotb/ahb_qspi_cocotb.v index 4a353908ecfdf3f84c99cab183441518adf64686..8f216ea27ffb7ad418beade28500013def24adbe 100644 --- a/verif/cocotb/ahb_qspi_cocotb.v +++ b/verif/cocotb/ahb_qspi_cocotb.v @@ -1,4 +1,4 @@ -`timescale 1ns/10ps +`timescale 1ns/10ps module ahb_qspi_cocotb( input wire HCLK, @@ -48,7 +48,7 @@ wire [31:0] PWDATA; wire [3:0] PSTRB; wire [31:0] PRDATA; wire PREADY; -wire PSLVERR; +wire PSLVERR; cmsdk_ahb_to_apb #( .ADDRWIDTH(16), @@ -110,7 +110,7 @@ top_ahb_qspi u_top_ahb_qspi( .HREADY(data_HREADY), .HREADYOUT(data_HREADY), .HRESP(data_HRESP), - + .PADDR(PADDR), .PPROT(PPROT), .PSEL(PSEL), @@ -120,7 +120,7 @@ top_ahb_qspi u_top_ahb_qspi( .PSTRB(PSTRB), .PRDATA(PRDATA), .PREADY(PREADY), - .PSLVERR(PSLVERR), + .PSLVERR(PSLVERR), .QSPI_SCLK(QSPI_SCLK), .QSPI_nCS(QSPI_nCS), .QSPI_IO_o(QSPI_IO_o), @@ -132,7 +132,7 @@ top_ahb_qspi u_top_ahb_qspi( initial begin $sdf_annotate("/home/dwn1c21/SoC-Labs/ahb_qspi/imp/ASIC/top_ahb_qspi.sdf", u_top_ahb_qspi); end -`endif +`endif assign QSPI_IO[0] = QSPI_IO_e[0] ? QSPI_IO_o[0] : 1'bz; assign QSPI_IO[1] = QSPI_IO_e[1] ? QSPI_IO_o[1] : 1'bz; @@ -149,10 +149,10 @@ sst26vf064b u_sst26vf064b( .SIO(QSPI_IO), .CEb(QSPI_nCS) ); -defparam u_sst26vf064b.I0.Tbe = 1_000; -defparam u_sst26vf064b.I0.Tse = 1_000; +defparam u_sst26vf064b.I0.Tbe = 1_000; +defparam u_sst26vf064b.I0.Tse = 1_000; defparam u_sst26vf064b.I0.Tsce = 1_000; -defparam u_sst26vf064b.I0.Tpp = 1_000; -defparam u_sst26vf064b.I0.Tws = 1000; +defparam u_sst26vf064b.I0.Tpp = 1_000; +defparam u_sst26vf064b.I0.Tws = 1000; -endmodule \ No newline at end of file +endmodule diff --git a/verif/cocotb/ahb_qspi_tests.py b/verif/cocotb/ahb_qspi_tests.py index 3f2488ece097173c6afd3c832bbf8db11a5fda09..7290ceaa94068b963e953d3cd7ff1da4fc15debf 100644 --- a/verif/cocotb/ahb_qspi_tests.py +++ b/verif/cocotb/ahb_qspi_tests.py @@ -356,7 +356,7 @@ async def QSPI_TESTS(dut): @cocotb.test() async def QSPI_READ_TESTS(dut): n_reads = 64 - + base_addr = 0x400 tb = TB(dut) await tb.cycle_reset() @@ -380,17 +380,17 @@ async def QSPI_READ_TESTS(dut): await SPI_WRITE_ENABLE(dut, tb) tb.log.info("Clear Protection and enable Write") await SPI_CLR_PROTECTION(dut, tb) - await SPI_WRITE_ENABLE(dut, tb) - SPI_STAT = await QPI_READ_STAT_REG(dut, tb) - WEL = SPI_STAT & (1<<25) - assert WEL != 0 tb.log.info("Page program 512x 16-bytes") data = [0x13243546, 0x97867564, 0x89ABCDEF, 0x01234567] for i in range(0,n_reads): - addr = i*16 - await QPI_PAGE_PROGRAM(dut, tb, addr=addr, words=data, n_words=4) + addr = i*16 + base_addr await SPI_WRITE_ENABLE(dut, tb) + SPI_STAT = await QPI_READ_STAT_REG(dut, tb) + WEL = SPI_STAT & (1<<25) + assert WEL != 0 + await QPI_PAGE_PROGRAM(dut, tb, addr=addr, words=data, n_words=4) + await SPI_WRITE_DISABLE(dut,tb) tb.log.info("Programmed %d words", i) await SPI_WRITE_DISABLE(dut,tb) @@ -407,7 +407,7 @@ async def QSPI_READ_TESTS(dut): tb.log.info("Read %d x 16-bytes over APB in continous read mode",n_reads) start_time = cocotb.utils.get_sim_time(units='ns') for i in range(0,n_reads): - addr = i*16 + addr = i*16 + base_addr rDATA = await QPI_READ_WORDS(dut, tb, addr=addr, n_words=4, cont_read=True) assert rDATA==data tb.log.info("Read %d words", i) @@ -444,7 +444,7 @@ async def QSPI_READ_TESTS(dut): tb.log.info("Read %d x 16-bytes over AHB (uncached)",n_reads) start_time_uncached = cocotb.utils.get_sim_time(units='ns') for i in range(0,n_reads*4): - data = await tb.data_ahb_master.read(i*4,4) + data = await tb.data_ahb_master.read(i*4 + base_addr,4) end_time_uncached = cocotb.utils.get_sim_time(units='ns') dt_uncached = end_time_uncached - start_time_uncached await Timer(time=10, units='us') @@ -452,7 +452,7 @@ async def QSPI_READ_TESTS(dut): tb.log.info("Read 512x 16-bytes over AHB (cached)") start_time_cached = cocotb.utils.get_sim_time(units='ns') for i in range(0,n_reads*4): - data = await tb.data_ahb_master.read(i*4,4) + data = await tb.data_ahb_master.read(i*4+ base_addr,4) end_time_cached = cocotb.utils.get_sim_time(units='ns') dt_cached = end_time_cached - start_time_cached diff --git a/verif/cocotb/makefile b/verif/cocotb/makefile index c4a1c90c6d92e16519e5de9d7b5166753afa4104..247b83effb71f3ea93634c41d0cec992a174b4b7 100644 --- a/verif/cocotb/makefile +++ b/verif/cocotb/makefile @@ -34,7 +34,7 @@ MODULE = ahb_qspi_tests VERILOG_SOURCES += $(SOCLABS_AHB_QSPI_DIR)/verif/cocotb/ahb_qspi_cocotb.v ifeq ($(GATE_SIMS),yes) - EXTRA_ARGS += +define+ARM_UD_MODEL +# EXTRA_ARGS += +define+ARM_UD_MODEL EXTRA_ARGS += +define+GATE_SIMS endif