SmartFusion2- AMBA APB, Custom Peripheral: Difference between revisions

From ift
No edit summary
No edit summary
Line 63: Line 63:
   
   
  # add your BFM commands below:  
  # add your BFM commands below:  
 
  memmap apb_to_dcs_0 0x50000000;
  memmap apb_to_dcs_0 0x50000000;
   
   

Revision as of 09:14, 30 September 2013

Intro

This tutorial explains how to implement and test a custom AMBA APB (Advanced Peripheral Bus) module. The tutorial will create a SmartFusion2 MSS (Microcontroller Subsystem) with APB bus interface. The custom module will be connected to the APB bus to map from APB bus to a DCS bus. Connected to the DCS part there will be a test module, which you can write and read from. The MSS will also have a GPIO module connected to a LED and serial UART peripheral.

Before you start

Make sure you have installed Libero and have a valid license, as described in SmartFusion2. This tutorial assumes Libero v11.1 is used and that you are using the SmartFusion2 starter kit from Microsemi.

Create new project

Start Libero SoC v11.1. Press Project and New Project. Name your project APB_custom_peripheral. Use system tools SmartFusion2 Microcontroller Subsystem (MSS).
New project.jpg

Modify MSS

Double click on the MSS component to modify it. A new window will open. Enable/Disable peripherals until your MSS looks like the image below.
Tip: click View > Maximize Work Area or CTRL+W to expand the working area while enabling and disabling the MSS peripherals.
Modified MSS.jpg

Click on the configuration button for the enabled peripherals, and configure them like the following images
MSS CCC.jpg
MSS CCC
MSS reset.jpg
Reset Controller
FIC0.jpg
FIC 0
MMUART.jpg
MMUART 0

Your MSS should now look like this:
MSS finished.jpg

SmartDesign

Close the MSS configuration window. On the SmartDesign canvas, right click on the MSS component and Update instance(s) with Latest Component.
From Catalog add the following components: CoreAPB3 from Bus Interfaces,
CoreAPB3 config.jpg

Chip Oscillators and Clock Conditioning Circuitry from Clock & Management
Chip osc.jpgCCC core.jpg

Now import all the vhdl files which are included at the bottom of the page. Save the files with the names specified in the text. The files can be included by clicking File, Import, HDL Source Files. These files can now be found under hdl in the Files tab and in Design Hierarchy. In Design Hierarchy right click on DCS_test, choose Create Core from HDL. Answer No to question about adding bus interfaces to core. Right click on apb_to_dcs and choose Create Core from HDL. Choose Yes on question about adding bus interfaces to core. Choose Add/Edit bus interfaces.... In next window, click Add Bus Interface..., choose APB, AMBA, AMBA2, slave. Connect PSELx to psel.
Module bus interface.jpg

Add the modules you made to the SmartDesign. In design canvas, right click and choose Auto Connect followed by Auto Arrange Instances. Right click on MSS_RESET_N_F2M and GPIO_FABRIC and select Promote to Top Level. Right click on reset_from_siu and choose Tie Low. Connect the unconnected wires. The result should look something like the image below
SmartDesign finished.jpg

Generate component by clicking SmartDesign and Generate Component.

Pre-synthesized Simulation

To simulate the functionality of the abp_to_dcs module, normal APB bus functionality from the MSS should be used. To do this you can add a .bfm file (Bus Functional Model). Copy the following to the User.bfm in your project, located under Files and simulation.

#===========================================================
# Enter your BFM commands in this file. 
#
# Syntax: 
# ------- 
#
# memmap    resource_name base_address;
#
# write     width resource_name byte_offset data;
# read      width resource_name byte_offset;
# readcheck width resource_name byte_offset data;
#
#===========================================================

#include "subsystem.bfm"

procedure user_main;

# perform subsystem initialization routine
#  call subsystem_init;  

# add your BFM commands below: 

memmap apb_to_dcs_0 0x50000000;


write w apb_to_dcs_0 0x0 0x12345678;
write w apb_to_dcs_0 0x4 0xBEEFFACE;
write w apb_to_dcs_0 0x8 0xABCDEF12;
write w apb_to_dcs_0 0xC 0x44556622;



readcheck w apb_to_dcs_0 0x0 0x12345678;
readcheck w apb_to_dcs_0 0x4 0xBEEFFACE;
readcheck w apb_to_dcs_0 0x8 0xABCDEF12;
readcheck w apb_to_dcs_0 0xC 0x44556622;



write w apb_to_dcs_0 0x10 0x98765432;
write w apb_to_dcs_0 0x14 0xABBABABE;
write w apb_to_dcs_0 0x18 0x12345ABC;
write w apb_to_dcs_0 0x1C 0xDEADBEEF;

readcheck w apb_to_dcs_0 0x10 0x98765432;
readcheck w apb_to_dcs_0 0x14 0xABBABABE;
readcheck w apb_to_dcs_0 0x18 0x12345ABC;
readcheck w apb_to_dcs_0 0x1C 0xDEADBEEF;




return

Next, go to Project and then Project Settings. Choose DO File under Simulation Options. You can use the run.do file supplied in the .zip. If you want to use automatic .do file and add signals yourself you can use standard settings, but be sure to change simulation time to 150 us. The system has got some initialization time, which takes around 120 us with this setup, before the bus signals start to change.

Double click on Simulate in Design Flow under Verify Pre-Synthesized Design which will start ModelSim.
Presynth transcript.jpg
Presynth wave.jpg

Constraints, Synthesize and Place&Route

Go to Project, Project Settings, Device I/O Settings and set Default I/O Technology to LVCMOS 3.3V.

Import the Physical Design Constraint file, Fabric_top.io, by clicking File, Import, I/O Constraints (PDC) Files. Once this is included, scroll in Design Flow to Place and Route and right click. Select Configure Options and uncheck Timing Driven.

Now you can click on the "Play" button, Generate Programming Data. This process will take some time. If all succeeds, you should be able to program your FPGA. Program it by connecting power and FlashPro Programmer and click on Run Programming Action.

Create Test Software

To write test application software, scroll to the bottom of the Design Flow and click Write Application Code. This will open SoftConsole from Microsemi. You can replace the main.c with the main.c supplied in the .zip file. The application will then write data to RAM and then read it. A LED is linked to the LSB of the read data, which cause the LED to blink. Data read on first memory location is written to the UART, and can be observed with a serial terminal program. Note that serial data sent on the UART is counting binary, and ASCII format is not implemented. The UART also sends out a message on the UART if the read data is different from expected.

Build Debug Application by pressing Debug Configurations and then choose Microsemi Cortex M3 Target. Press Apply, build debug target and launch debug target.
Debug configuration.jpg
Debugging.jpg
Debug binary count.jpg

-------------------------------------------------------------------------------
-- Title      : APB_to_DCS
-- Project    : RCU2
-------------------------------------------------------------------------------
-- File             : apb_to_dcs.vhd
-- Last edited by   : Christian Torgersen
-- Last update      : 30.09.2013 - 09:26
-- Current Revision : 1.0
-------------------------------------------------------------------------------
-- Description      : Mapping between AMBA APB and DCS bus.  
-------------------------------------------------------------------------------
-- Revision History : 
-------------------------------------------------------------------------------
--
-- This file is property of and copyright by the Instrumentation and 
-- Electronics Section, Dep. of Physics and Technology
-- University of Bergen, Norway
-- This file has been written by Christian Torgersen
-- Christian.torgersen@student.uib.no
--
-- Permission to use, copy, modify and distribute this firmware and its  
-- documentation strictly for non-commercial purposes is hereby granted  
-- without fee, provided that the above copyright notice appears in all  
-- copies and that both the copyright notice and this permission notice  
-- appear in the supporting documentation. The authors make no claims    
-- about the suitability of this software for any purpose. It is         
-- provided "as is" without express or implied warranty.                 
--
-------------------------------------------------------------------------------

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;

library work;
use work.dcs_interface_pkg.all;

entity apb_to_dcs is
port(
	--APB input control signals
	penable				: in		std_logic;						-- APB enable signal. Asserted high on second pulse
	psel				: in		std_logic;						-- APB slave select from master
	pwrite				: in		std_logic;						-- APB direction setting
	
	--APB input and addr
	paddr				: in		std_logic_vector(31 downto 0);	-- APB address
	pwdata				: in		std_logic_vector(31 downto 0);	-- APB write data
	
	--APB output signals
	prdata				: out	std_logic_vector(31 downto 0);	-- APB read data
	pready				: out	std_logic;						-- APB hold signal, for read/write more than 2 cycles
	pslverr				: out	std_logic;						-- APB slave error signal

    clk                 : in    std_logic;                     -- global clock (40 MHz)
    reset_n             : in    std_logic;                     -- asynch reset, negative polarity
    reset_from_siu      : in    std_logic;                     -- asynch reset from SIU, positive polarity 
	
	--internal resets
	global_reset        : out   std_logic;                     -- global reset, positive polarity
    rcu_reset           : out   std_logic;                     -- reset for RCU Xilinx fw,  positive polarity
    fec_reset           : out   std_logic;                     -- reset for FEC, positive polarity
	
	--DCS bus signals
	we_dcs              : out   std_logic;                     -- write enable to the internal modules
	dcs_busBadd         : out   std_logic_vector(15 downto 0); -- address passed through
	dcs_busBdata_in     : in    std_logic_vector(31 downto 0); -- data from internal modules to DCS
	dcs_busBdata_out    : out   std_logic_vector(31 downto 0); -- data to internal modules from DCS
	dcs_bi              : out   std_logic;                     -- interrupt in case DCS needs bus
	dcs_bg              : in    std_logic;                     -- dcs bus grant from arbiter
    dcs_br              : out   std_logic;	                   -- dcs bus request to arbiter
    siu_bg              : in    std_logic                      -- siu bus grant from arbiter
	);
end apb_to_dcs; 

architecture arc of apb_to_dcs is

--signal declarations
	type   state is (s_idle, s_wait_for_grant, s_grant, s_error);
	signal current_state, next_state : state;
	
--	signal resetting        : std_logic; -- high when the resetting addresses are received, only used by dcs_addr
	signal timeout			:std_logic;
	signal timeout_cnt		:std_logic_vector(6 downto 0);
	signal timeout_cnt_en	:std_logic;
	signal we_dcs_i			:std_logic;	
	
begin
	
--combinatorics
timeout		<= timeout_cnt(6);

we_dcs				<= we_dcs_i;
dcs_br				<= '1' when (psel = '1'and (next_state = s_wait_for_grant or next_state = s_grant)) else '0';
 
dcs_busBadd			<= paddr(15 downto 0);										--linking between APB and      dcs addresses
dcs_busBdata_out	<= pwdata when (pwrite='1' and dcs_bg= '1') else (others => '0');				-- APB to DCS data	
prdata				<= dcs_busBdata_in when (pwrite ='0' and dcs_bg= '1') else (others => '0');		-- DCS to APB data 

-- purpose: timeout counter for transaction
p_timeout_cnt: process(clk, reset_n, reset_from_siu)
begin
  if (reset_n = '0' or reset_from_siu = '1') then
    timeout_cnt <= (others => '0');
  elsif rising_edge(clk) then
    if (timeout_cnt_en = '1') then
	    timeout_cnt <= timeout_cnt + 1;
	  else
	    timeout_cnt <= (others => '0');
	  end if;
  end if;
end process p_timeout_cnt;

-- purpose: state machine driver
p_state_driver: process(clk, reset_n, reset_from_siu)
begin
  if (reset_n = '0' or reset_from_siu = '1') then
    current_state <= s_idle;
  elsif rising_edge(clk) then
    current_state <= next_state;
  end if;
end process p_state_driver;	

--purpose: set next state	
p_next_state: process(current_state, dcs_bg, timeout, psel, penable)		
begin
  case current_state is
    when s_idle =>
	  if (dcs_bg = '1' and psel = '1' and penable = '0') then		-- giving two cycle read/write possibility
		next_state <= s_grant;
      elsif (dcs_bg = '0' and psel = '1' and penable = '0') then	--Three or more cycle read/write
	    next_state <= s_wait_for_grant;
	  else
	    next_state <= s_idle;
	  end if;
  
    when s_wait_for_grant => 
      if (dcs_bg = '1') then 
  	    next_state <= s_grant;
	    elsif (timeout = '1') then
	      next_state <= s_error;
	    else
	      next_state <= s_wait_for_grant;
      end if;
  
    when  s_grant =>
      next_state <= s_idle;
   
    when s_error =>
      next_state <= s_idle;
    
	when others =>
	  next_state <= s_idle;
  end case;
end process p_next_state;	
	
-- purpose: set outputs of module and internal signals
p_output: process(clk, reset_n, reset_from_siu)
begin
  if (reset_n = '0' or reset_from_siu = '1') then
    timeout_cnt_en  <= '0';
	we_dcs_i        <= '0';     
	pslverr			<= '0';
 	pready			<= '1';

  elsif rising_edge(clk) then
    	--defaults
	case current_state is
    when s_idle => 
 		pslverr <= '0';
		timeout_cnt_en  <= '0';    

     when s_wait_for_grant =>
	  	    
	when s_grant =>
      	we_dcs_i        <= '0';
	  	pready			<= '1';
	    	  
	when s_error => 
		pslverr <= '1';
		pready	<= '1';
		timeout_cnt_en <= '0';
		
	when others =>
	    --defaults
    end case;

  	case next_state is
	when s_idle =>
		pready <= '1';

	when s_wait_for_grant =>
		timeout_cnt_en  <= '1';
		pready <= '0';

	when s_grant =>
    		timeout_cnt_en  <= '0';
		pready <= '1';
		we_dcs_i <= pwrite;
	when others =>
	end case;
  end if ;
end process p_output;	 

--purpose: resets
p_reset : process(clk, reset_n)
begin
  if (reset_n = '0') then
    global_reset    <= '1'; 		-- send reset when the circuit is being globally reseted by the reset line
    rcu_reset       <= '1';  
    fec_reset       <= '1'; 
  elsif rising_edge(clk) then
    global_reset    <= '0'; 		-- send reset when the circuit is being globally reseted by the reset line
    rcu_reset       <= '0';  
    fec_reset       <= '0'; 
    if (we_dcs_i = '1') then
      case (paddr(15 downto 0)) is
        when c_global_reset =>
          global_reset <= '1';
        when c_rcu_reset =>
          rcu_reset    <= '1';
        when c_fec_reset =>
          fec_reset    <= '1';
        when others =>
          -- do nothing
      end case;
    end if;      
  end if;
end process p_reset; 

--Purpose: Set up bus interrupt
p_bus_int: process (clk, reset_n, reset_from_siu)
begin
	if( reset_n = '0' or reset_from_siu = '1') then
		dcs_bi <= '0';
	elsif (rising_edge(clk)) then
		if(siu_bg = '0' or dcs_bg = '1') then		--do not interrupt if we have grant
			dcs_bi <= '0';
		elsif (paddr (15 downto 0) = c_arbiter_irq) then
			dcs_bi <= '1';
		end if;
	end if;
end process p_bus_int;
	
end architecture arc;
-------------------------------------------------------------------------------
-- Title      : DCS interface Package
-- Project    : RCU DCS interface
-------------------------------------------------------------------------------
-- File             : $RCSfile: dcs_interface_pkg.vhd,v $
-- Last edited by   : $Author: alme $
-- Last update      : $Date: 2008/02/15 12:23:43 $
-- Current Revision : $Revision: 1.4 $
-------------------------------------------------------------------------------
-- Description      : Constant and function library for RCU Trigger Receiver
--                    design  
-------------------------------------------------------------------------------
-- Revision History : 
-- http://portal1.ift.uib.no/cgi-bin/viewcvs.cgi/vhdlcvs/rcu_cpld/
-------------------------------------------------------------------------------
--
-- This file is property of and copyright by the Instrumentation and 
-- Electronics Section, Dep. of Physics and Technology
-- University of Bergen, Norway, 2005
-- This file has been written by Johan Alme
-- Johan.Alme@ift.uib.no
--
-- Permission to use, copy, modify and distribute this firmware and its  
-- documentation strictly for non-commercial purposes is hereby granted  
-- without fee, provided that the above copyright notice appears in all  
-- copies and that both the copyright notice and this permission notice  
-- appear in the supporting documentation. The authors make no claims    
-- about the suitability of this software for any purpose. It is         
-- provided "as is" without express or implied warranty.                 
--
-------------------------------------------------------------------------------

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;

package dcs_interface_pkg is
 
 -- address information (this should be moved to a global adress mapping definition file.)
  -- memory spaces.
  -- Safety module address. Always ack these.
  constant c_MSM_space          : std_logic_vector(3 downto 0):=X"8";
  -- Actel space should NEVER be acked
  constant c_Actel_space        : std_logic_vector(3 downto 0):=X"B";
  -- treat as normal except for the subadresses 0xA00 and 0xA01 that should not be acked.
  constant c_trigger_space      : std_logic_vector(3 downto 0):=X"4"; 
  -- sub adresses
  -- belongs to trigger space. The two addresses are defined on the DCS board and should not be acked. 
  constant c_dcsSetBunchReset   : std_logic_vector(11 downto 0):= X"A00";
  constant c_dcsSetEventReset   : std_logic_vector(11 downto 0):= X"A01";
  
  constant c_global_reset       : std_logic_vector(15 downto 0):= X"5300";
  constant c_fec_reset          : std_logic_vector(15 downto 0):= X"5301";
  constant c_rcu_reset          : std_logic_vector(15 downto 0):= X"5302";
  constant c_arbiter_irq        : std_logic_vector(15 downto 0):= X"5310"; -- interrupts SIU grant
  constant c_grant              : std_logic_vector(15 downto 0):= X"5311"; -- grant information given
  
  --Constant defining mem mapped mode on which the interface should be active
  constant c_memMappedMode0      : std_logic_vector(1 downto 0) := "00";
  constant c_memMappedMode1      : std_logic_vector(1 downto 0) := "11";
       
end package dcs_interface_pkg; 
--------------------------------------------------------------------------------
-- Company: University of Bergen
--
-- File: DCS_test.vhd
-- File history:
--      <Revision number>: <Date>: <Comments>
--      <Revision number>: <Date>: <Comments>
--      <Revision number>: <Date>: <Comments>
--
-- Description: 
--
-- Component used to test functionality of apb_to_dcs bridge
--
-- Targeted device: <Family::SmartFusion2> <Die::M2S050T_ES> <Package::896 FBGA>
-- Author: Christian Torgersen
--
--------------------------------------------------------------------------------

library IEEE;

use IEEE.std_logic_1164.all;

entity DCS_test is
port (
	clk                 : in    std_logic;                     -- global clock (40 MHz)
	reset_n             : in    std_logic;                     -- asynch reset, negative polarity

	global_reset        : in   std_logic;                     -- global reset, positive polarity
    rcu_reset           : in   std_logic;                     -- reset for RCU Xilinx fw,  positive polarity
    fec_reset           : in   std_logic;                     -- reset for FEC, positive polarity

	we_dcs              : in   std_logic;                     -- write enable to the internal modules
	dcs_busBadd         : in    std_logic_vector(15 downto 0); -- address passed through
	dcs_busBdata_in     : in    std_logic_vector(31 downto 0); -- data from internal modules to DCS
	dcs_busBdata_out    : out    std_logic_vector(31 downto 0); -- data to internal modules from DCS
	dcs_bi              : in    std_logic;                     -- interrupt in case DCS needs bus
	dcs_bg              : out   std_logic;                     -- dcs bus grant from arbiter
    dcs_br              : in    std_logic;	                   -- dcs bus request to arbiter
    siu_bg              : out    std_logic                      -- siu bus grant from arbiter 

);
end DCS_test;
architecture arch of DCS_test is
   -- signal, component etc. declarations
signal		data_outs :std_logic_vector(31 DOWNTO 0);
signal		wr_enable, rd_enable : std_logic;


component mtest
port(
		clk      : IN std_logic;
		nreset   : IN std_logic;
		wr_en    : IN std_logic;
		rd_en	 : IN std_logic;
		address  : IN std_logic_vector(7 DOWNTO 0);
		data_in  : IN std_logic_vector(31 DOWNTO 0);
		data_out : OUT std_logic_vector(31 DOWNTO 0)
);
end component;


begin

mtest_map: mtest port map(
	clk 		=> clk, 
	nreset		=> reset_n, 
	wr_en 		=> wr_enable,
	rd_en		=> rd_enable,
	address		=> dcs_busBadd(7 downto 0),
	data_in		=> dcs_busBdata_in, 
	data_out	=> dcs_busBdata_out 
); 

--dcs_busBdata_out <= data_outs;
siu_bg <= '0';
rd_enable <= not(we_dcs);
wr_enable <= we_dcs;

-- to be used if checking two cycle read/write:
--dcs_bg <= '1';

--	Used to test arbitration and wait states:
p_arbitration: process (clk, reset_n)
begin
	if reset_n = '0' then
		dcs_bg <= '1';
		data_outs <= (others => '0');
		
	elsif rising_edge(clk) then
		if (dcs_br = '1') then
			dcs_bg <= '1';
		else
			dcs_bg<= '0';
			
		end if;
	end if;

end process p_arbitration;


end arch;
--------------------------------------------------------------------------------
-- Company: <Name>
--
-- File: mtest.vhd
-- File history:
--      <Revision number>: <Date>: <Comments>
--      <Revision number>: <Date>: <Comments>
--      <Revision number>: <Date>: <Comments>
--
-- Description: 
--
-- <Description here>
--
-- Targeted device: <Family::SmartFusion2> <Die::M2S050T_ES> <Package::896 FBGA>
-- Author: <Name>
--
--------------------------------------------------------------------------------

library IEEE;

use IEEE.std_logic_1164.all;
use ieee.numeric_std.all;

entity mtest is
port (
		clk      : IN std_logic;
		nreset   : IN std_logic;
		wr_en    : IN std_logic;
		rd_en	 : IN std_logic;
		address  : IN std_logic_vector(7 DOWNTO 0);
		data_in  : IN std_logic_vector(31 DOWNTO 0);
		data_out : OUT std_logic_vector(31 DOWNTO 0)
);
end mtest;
architecture arch of mtest is
-- signal, component etc. declarations
type memory IS ARRAY (0 TO 31) of std_logic_vector(31 DOWNTO 0);
--type memory IS ARRAY (31 DOWNTO 0) of std_logic_vector;
signal myram: memory;
--attribute ram_init_file: STRING;
--attribute ram_init_file OF myram: SIGNAL IS "ram_contents.mif";
begin
	-- generation of data_out
	process(clk,nreset)
	begin
		if (nreset='0') then
			data_out <= x"0000_0000";
	    elsif (clk'EVENT AND clk='1') then
--			if (nreset='0') then
--				data_out <= x"0000_0000";
--			elsif(rd_en='1') then		
			if(rd_en='1') then		
				data_out <= myram(to_integer(unsigned(address)));
			end if;
		end if;
	end process; 
	-- writing data to memory
 	process(clk,nreset)
 	begin
		if (nreset='0') then
			myram(to_integer(unsigned(address))) <= x"DEAD_BEEF";
		elsif (clk'EVENT AND clk='1') then
--			if (nreset='0') then
--				myram(to_integer(unsigned(address))) <= x"DEAD_BEEF";
--			elsif(wr_en='1') then
			if (wr_en='1') then
				myram(to_integer(unsigned(address))) <= data_in;
			end if;
		end if;
	end process;
end arch;