很多朋友對于異步fifo的設計和設計同步fifo,每100個cycle不太懂,今天就由小編來為大家分享,希望可以幫助到大家,下面一起來看看吧!
碩士生找工作的時候把異步fifo寫成一個項目經歷,是不是顯得很low啊
異步fifo本身不low,一個進程有異步,一臺電腦有異步,一個集群有異步,異構集群也有異步,應用場景大了,能做到fifo本身就是本事。以前網絡不好的時候,2g環境和wifi經常切換,寫聊天軟件,udp掉包,粘包,亂序,這種現在很少遇到的問題,那時候解決了也花了很久。不依賴他人的框架,能完成的設計都不low。
Java多線程同步內部如何實現的
提示
請帶著這些問題繼續后文,會很大程度上幫助你更好的理解相關知識點。@pdai
為什么要有線程池?Java是實現和管理線程池有哪些方式?請簡單舉例如何使用。為什么很多公司不允許使用Executors去創建線程池?那么推薦怎么使用呢?ThreadPoolExecutor有哪些核心的配置參數?請簡要說明ThreadPoolExecutor可以創建哪是哪三種線程池呢?當隊列滿了并且worker的數量達到maxSize的時候,會怎么樣?說說ThreadPoolExecutor有哪些RejectedExecutionHandler策略?默認是什么策略?簡要說下線程池的任務執行機制?execute–>addWorker–>runworker(getTask)線程池中任務是如何提交的?線程池中任務是如何關閉的?在配置線程池的時候需要考慮哪些配置因素?如何監控線程池的狀態?為什么要有線程池線程池能夠對線程進行統一分配,調優和監控:
降低資源消耗(線程無限制地創建,然后使用完畢后銷毀)提高響應速度(無須創建線程)提高線程的可管理性ThreadPoolExecutor例子Java是如何實現和管理線程池的?
從JDK5開始,把工作單元與執行機制分離開來,工作單元包括Runnable和Callable,而執行機制由Executor框架提供。
WorkerThreadSimpleThreadPool
程序中我們創建了固定大小為五個工作線程的線程池。然后分配給線程池十個工作,因為線程池大小為五,它將啟動五個工作線程先處理五個工作,其他的工作則處于等待狀態,一旦有工作完成,空閑下來工作線程就會撿取等待隊列里的其他工作進行執行。
這里是以上程序的輸出。
輸出表明線程池中至始至終只有五個名為"pool-1-thread-1"到"pool-1-thread-5"的五個線程,這五個線程不隨著工作的完成而消亡,會一直存在,并負責執行分配給線程池的任務,直到線程池消亡。
Executors類提供了使用了ThreadPoolExecutor的簡單的ExecutorService實現,但是ThreadPoolExecutor提供的功能遠不止于此。我們可以在創建ThreadPoolExecutor實例時指定活動線程的數量,我們也可以限制線程池的大小并且創建我們自己的RejectedExecutionHandler實現來處理不能適應工作隊列的工作。
這里是我們自定義的RejectedExecutionHandler接口的實現。
RejectedExecutionHandlerImpl.javaThreadPoolExecutor提供了一些方法,我們可以使用這些方法來查詢executor的當前狀態,線程池大小,活動線程數量以及任務數量。因此我是用來一個監控線程在特定的時間間隔內打印executor信息。
MyMonitorThread.java這里是使用ThreadPoolExecutor的線程池實現例子。
WorkerPool.java注意在初始化ThreadPoolExecutor時,我們保持初始池大小為2,最大池大小為4而工作隊列大小為2。因此如果已經有四個正在執行的任務而此時分配來更多任務的話,工作隊列將僅僅保留他們(新任務)中的兩個,其他的將會被RejectedExecutionHandlerImpl處理。
上面程序的輸出可以證實以上觀點。
注意executor的活動任務、完成任務以及所有完成任務,這些數量上的變化。我們可以調用shutdown()方法來結束所有提交的任務并終止線程池。
ThreadPoolExecutor使用詳解其實java線程池的實現原理很簡單,說白了就是一個線程集合workerSet和一個阻塞隊列workQueue。當用戶向線程池提交一個任務(也就是線程)時,線程池會先將任務放入workQueue中。workerSet中的線程會不斷的從workQueue中獲取線程然后執行。當workQueue中沒有任務的時候,worker就會阻塞,直到隊列中有任務了就取出來繼續執行。
Execute原理當一個任務提交至線程池之后:
線程池首先當前運行的線程數量是否少于corePoolSize。如果是,則創建一個新的工作線程來執行任務。如果都在執行任務,則進入2.判斷BlockingQueue是否已經滿了,倘若還沒有滿,則將線程放入BlockingQueue。否則進入3.如果創建一個新的工作線程將使當前運行的線程數量超過maximumPoolSize,則交給RejectedExecutionHandler來處理任務。當ThreadPoolExecutor創建新線程時,通過CAS來更新線程池的狀態ctl.
參數corePoolSize線程池中的核心線程數,當提交一個任務時,線程池創建一個新線程執行任務,直到當前線程數等于corePoolSize,即使有其他空閑線程能夠執行新來的任務,也會繼續創建線程;如果當前線程數為corePoolSize,繼續提交的任務被保存到阻塞隊列中,等待被執行;如果執行了線程池的prestartAllCoreThreads()方法,線程池會提前創建并啟動所有核心線程。workQueue用來保存等待被執行的任務的阻塞隊列.在JDK中提供了如下阻塞隊列:具體可以參考JUC集合:BlockQueue詳解ArrayBlockingQueue:基于數組結構的有界阻塞隊列,按FIFO排序任務;LinkedBlockingQueue:基于鏈表結構的阻塞隊列,按FIFO排序任務,吞吐量通常要高于ArrayBlockingQueue;SynchronousQueue:一個不存儲元素的阻塞隊列,每個插入操作必須等到另一個線程調用移除操作,否則插入操作一直處于阻塞狀態,吞吐量通常要高于LinkedBlockingQueue;PriorityBlockingQueue:具有優先級的無界阻塞隊列;LinkedBlockingQueue比ArrayBlockingQueue在插入刪除節點性能方面更優,但是二者在put(),take()任務的時均需要加鎖,SynchronousQueue使用無鎖算法,根據節點的狀態判斷執行,而不需要用到鎖,其核心是Transfer.transfer().
maximumPoolSize線程池中允許的最大線程數。如果當前阻塞隊列滿了,且繼續提交任務,則創建新的線程執行任務,前提是當前線程數小于maximumPoolSize;當阻塞隊列是無界隊列,則maximumPoolSize則不起作用,因為無法提交至核心線程池的線程會一直持續地放入workQueue.keepAliveTime線程空閑時的存活時間,即當線程沒有任務執行時,該線程繼續存活的時間;默認情況下,該參數只在線程數大于corePoolSize時才有用,超過這個時間的空閑線程將被終止;unitkeepAliveTime的單位threadFactory創建線程的工廠,通過自定義的線程工廠可以給每個新建的線程設置一個具有識別度的線程名。默認為DefaultThreadFactoryhandler線程池的飽和策略,當阻塞隊列滿了,且沒有空閑的工作線程,如果繼續提交任務,必須采取一種策略處理該任務,線程池提供了4種策略:AbortPolicy:直接拋出異常,默認策略;CallerRunsPolicy:用調用者所在的線程來執行任務;DiscardOldestPolicy:丟棄阻塞隊列中靠最前的任務,并執行當前任務;DiscardPolicy:直接丟棄任務;當然也可以根據應用場景實現RejectedExecutionHandler接口,自定義飽和策略,如記錄日志或持久化存儲不能處理的任務。
三種類型newFixedThreadPool線程池的線程數量達corePoolSize后,即使線程池沒有可執行任務時,也不會釋放線程。
FixedThreadPool的工作隊列為無界隊列LinkedBlockingQueue(隊列容量為Integer.MAX_VALUE),這會導致以下問題:
線程池里的線程數量不超過corePoolSize,這導致了maximumPoolSize和keepAliveTime將會是個無用參數由于使用了無界隊列,所以FixedThreadPool永遠不會拒絕,即飽和策略失效newSingleThreadExecutor初始化的線程池中只有一個線程,如果該線程異常結束,會重新創建一個新的線程繼續執行任務,唯一的線程可以保證所提交任務的順序執行.
由于使用了無界隊列,所以SingleThreadPool永遠不會拒絕,即飽和策略失效
newCachedThreadPool線程池的線程數可達到Integer.MAX_VALUE,即2147483647,內部使用SynchronousQueue作為阻塞隊列;和newFixedThreadPool創建的線程池不同,newCachedThreadPool在沒有任務執行時,當線程的空閑時間超過keepAliveTime,會自動釋放線程資源,當提交新任務時,如果沒有空閑線程,則創建新線程執行任務,會導致一定的系統開銷;執行過程與前兩種稍微不同:
主線程調用SynchronousQueue的offer()方法放入task,倘若此時線程池中有空閑的線程嘗試讀取SynchronousQueue的task,即調用了SynchronousQueue的poll(),那么主線程將該task交給空閑線程.否則執行(2)當線程池為空或者沒有空閑的線程,則創建新的線程執行任務.執行完任務的線程倘若在60s內仍空閑,則會被終止.因此長時間空閑的CachedThreadPool不會持有任何線程資源.關閉線程池遍歷線程池中的所有線程,然后逐個調用線程的interrupt方法來中斷線程.
關閉方式-shutdown將線程池里的線程狀態設置成SHUTDOWN狀態,然后中斷所有沒有正在執行任務的線程.
關閉方式-shutdownNow將線程池里的線程狀態設置成STOP狀態,然后停止所有正在執行或暫停任務的線程.只要調用這兩個關閉方法中的任意一個,isShutDown()返回true.當所有任務都成功關閉了,isTerminated()返回true.
ThreadPoolExecutor源碼詳解幾個關鍵屬性內部狀態其中AtomicInteger變量ctl的功能非常強大:利用低29位表示線程池中線程數,通過高3位表示線程池的運行狀態:
RUNNING:-1<<COUNT_BITS,即高3位為111,該狀態的線程池會接收新任務,并處理阻塞隊列中的任務;SHUTDOWN:0<<COUNT_BITS,即高3位為000,該狀態的線程池不會接收新任務,但會處理阻塞隊列中的任務;STOP:1<<COUNT_BITS,即高3位為001,該狀態的線程不會接收新任務,也不會處理阻塞隊列中的任務,而且會中斷正在運行的任務;TIDYING:2<<COUNT_BITS,即高3位為010,所有的任務都已經終止;TERMINATED:3<<COUNT_BITS,即高3位為011,terminated()方法已經執行完成任務的執行execute–>addWorker–>runworker(getTask)
線程池的工作線程通過Woker類實現,在ReentrantLock鎖的保證下,把Woker實例插入到HashSet后,并啟動Woker中的線程。從Woker類的構造方法實現可以發現:線程工廠在創建線程thread時,將Woker實例本身this作為參數傳入,當執行start方法啟動線程thread時,本質是執行了Worker的runWorker方法。firstTask執行完成之后,通過getTask方法從阻塞隊列中獲取等待的任務,如果隊列中沒有任務,getTask方法會被阻塞并掛起,不會占用cpu資源;
execute()方法ThreadPoolExecutor.execute(task)實現了Executor.execute(task)
為什么需要doublecheck線程池的狀態?在多線程環境下,線程池的狀態時刻在變化,而ctl.get()是非原子操作,很有可能剛獲取了線程池狀態后線程池狀態就改變了。判斷是否將command加入workque是線程池之前的狀態。倘若沒有doublecheck,萬一線程池處于非running狀態(在多線程環境下很有可能發生),那么command永遠不會執行。
addWorker方法從方法execute的實現可以看出:addWorker主要負責創建新的線程并執行任務線程池創建新線程執行任務時,需要獲取全局鎖:
Worker類的runworker方法繼承了AQS類,可以方便的實現工作線程的中止操作;實現了Runnable接口,可以將自身作為一個任務在工作線程中執行;當前提交的任務firstTask作為參數傳入Worker的構造方法;一些屬性還有構造方法:
runWorker方法是線程池的核心:
線程啟動之后,通過unlock方法釋放鎖,設置AQS的state為0,表示運行可中斷;Worker執行firstTask或從workQueue中獲取任務:進行加鎖操作,保證thread不被其他線程中斷(除非線程池被中斷)檢查線程池狀態,倘若線程池處于中斷狀態,當前線程將中斷。執行beforeExecute執行任務的run方法執行afterExecute方法解鎖操作通過getTask方法從阻塞隊列中獲取等待的任務,如果隊列中沒有任務,getTask方法會被阻塞并掛起,不會占用cpu資源;
getTask方法
下面來看一下getTask()方法,這里面涉及到keepAliveTime的使用,從這個方法我們可以看出線程池是怎么讓超過corePoolSize的那部分worker銷毀的。
注意這里一段代碼是keepAliveTime起作用的關鍵:
allowCoreThreadTimeOut為false,線程即使空閑也不會被銷毀;倘若為ture,在keepAliveTime內仍空閑則會被銷毀。
如果線程允許空閑等待而不被銷毀timed==false,workQueue.take任務:如果阻塞隊列為空,當前線程會被掛起等待;當隊列中有任務加入時,線程被喚醒,take方法返回任務,并執行;
如果線程不允許無休止空閑timed==true,workQueue.poll任務:如果在keepAliveTime時間內,阻塞隊列還是沒有任務,則返回null;
任務的提交submit任務,等待線程池execute執行FutureTask類的get方法時,會把主線程封裝成WaitNode節點并保存在waiters鏈表中,并阻塞等待運行結果;FutureTask任務執行完成后,通過UNSAFE設置waiters相應的waitNode為null,并通過LockSupport類unpark方法喚醒主線程;在實際業務場景中,Future和Callable基本是成對出現的,Callable負責產生結果,Future負責獲取結果。
Callable接口類似于Runnable,只是Runnable沒有返回值。Callable任務除了返回正常結果之外,如果發生異常,該異常也會被返回,即Future可以拿到異步執行任務各種結果;Future.get方法會導致主線程阻塞,直到Callable任務執行完成;submit方法AbstractExecutorService.submit()實現了ExecutorService.submit()可以獲取執行完的返回值,而ThreadPoolExecutor是AbstractExecutorService.submit()的子類,所以submit方法也是ThreadPoolExecutor`的方法。
通過submit方法提交的Callable任務會被封裝成了一個FutureTask對象。通過Executor.execute方法提交FutureTask到線程池中等待被執行,最終執行的是FutureTask的run方法;
FutureTask對象publicclassFutureTask<V>implementsRunnableFuture<V>可以將FutureTask提交至線程池中等待被執行(通過FutureTask的run方法來執行)
內部狀態內部狀態的修改通過sun.misc.Unsafe修改
get方法內部通過awaitDone方法對主線程進行阻塞,具體實現如下:
如果主線程被中斷,則拋出中斷異常;
判斷FutureTask當前的state,如果大于COMPLETING,說明任務已經執行完成,則直接返回;如果當前state等于COMPLETING,說明任務已經執行完,這時主線程只需通過yield方法讓出cpu資源,等待state變成NORMAL;通過WaitNode類封裝當前線程,并通過UNSAFE添加到waiters鏈表;最終通過LockSupport的park或parkNanos掛起線程;run方法
FutureTask.run方法是在線程池中被執行的,而非主線程
通過執行Callable任務的call方法;如果call執行成功,則通過set方法保存結果;如果call執行有異常,則通過setException保存異常;任務的關閉shutdown方法會將線程池的狀態設置為SHUTDOWN,線程池進入這個狀態后,就拒絕再接受任務,然后會將剩余的任務全部執行完
shutdownNow做的比較絕,它先將線程池狀態設置為STOP,然后拒絕所有提交的任務。最后中斷左右正在運行中的worker,然后清空任務隊列。
更深入理解
為什么線程池不允許使用Executors去創建?推薦方式是什么?線程池不允許使用Executors去創建,而是通過ThreadPoolExecutor的方式,這樣的處理方式讓寫的同學更加明確線程池的運行規則,規避資源耗盡的風險。說明:Executors各個方法的弊端:
newFixedThreadPool和newSingleThreadExecutor:??主要問題是堆積的請求處理隊列可能會耗費非常大的內存,甚至OOM。newCachedThreadPool和newScheduledThreadPool:??主要問題是線程數最大數是Integer.MAX_VALUE,可能會創建數量非常多的線程,甚至OOM。推薦方式1首先引入:commons-lang3包
推薦方式2首先引入:com.google.guava包
推薦方式3spring配置線程池方式:自定義線程工廠bean需要實現ThreadFactory,可參考該接口的其它默認實現類,使用方式直接注入bean調用execute(Runnabletask)方法即可
配置線程池需要考慮因素
從任務的優先級,任務的執行時間長短,任務的性質(CPU密集/IO密集),任務的依賴關系這四個角度來分析。并且近可能地使用有界的工作隊列。
性質不同的任務可用使用不同規模的線程池分開處理:
CPU密集型:盡可能少的線程,Ncpu+1IO密集型:盡可能多的線程,Ncpu*2,比如數據庫連接池混合型:CPU密集型的任務與IO密集型任務的執行時間差別較小,拆分為兩個線程池;否則沒有必要拆分。監控線程池的狀態可以使用ThreadPoolExecutor以下方法:
getTaskCount()Returnstheapproximatetotalnumberoftasksthathaveeverbeenscheduledforexecution.getCompletedTaskCount()Returnstheapproximatetotalnumberoftasksthathavecompletedexecution.返回結果少于getTaskCount()。getLargestPoolSize()Returnsthelargestnumberofthreadsthathaveeversimultaneouslybeeninthepool.返回結果小于等于maximumPoolSizegetPoolSize()Returnsthecurrentnumberofthreadsinthepool.getActiveCount()Returnstheapproximatenumberofthreadsthatareactivelyexecutingtasks.參考文章《Java并發編程藝術》https://www.jianshu.com/p/87bff5cc8d8chttps://blog.csdn.net/programmer_at/article/details/79799267https://blog.csdn.net/u013332124/article/details/79587436https://www.journaldev.com/1069/threadpoolexecutor-java-thread-pool-example-executorservice由于問答代碼塊插入受限,部分代碼未完全展示,若有需要可閱讀原文:戳我閱讀原文
異步fifo同時讀寫有問題嗎
同時讀寫有問題,會影響數據一致性
芯片設計中,最難的是架構設計還是驗證
芯片設計這個行當,從大的方面講,主要分模擬和數字兩大塊,而每大塊又分前端和后端,我想大部分同學對這個肯定是非常清楚的,下面就數字電路聊聊芯片設計的一些事情,就是芯片設計有哪些活做,這并不是全面完整的系統介紹,只是個人的了解和總結,希望拋磚引玉,也許不全面,不正確,歡迎同學們指正和補充。說到數字芯片,不能不說FPGA,這種是可編程的數字電路,用法原理也不說了,數字電路設計的目標,就是把這些功能,做成我們自己專用的ASIC/SOC,這樣無論面積,成本或者安全性等等都能有保證。從流程上講,數字芯片設計的大致步驟就是系統與功能定義,RTL實現驗證,綜合及可測試性設計(synthesize,DFT),ATPG仿真,時序分析,到自動布局布線(APR).直至交付fab的GDS網表。這個流程是可以反復迭代的,當對于不同類型芯片,如純數ASIC或混合電路(mix-signal及系統級芯片(SOC),每一步的方法和具體實施流程上可能又有所差異.下面就這些基本流程分步談一些主要問題。
系統設計主要設計到功能定義及架構設計,總線架構的配置,模塊設計,數據流的分配,時鐘的設計等問題。總線包括模塊之間,模塊與MCU核之間,或者外部主機和芯片之間通信,或者測試需要等等一系列因素。時鐘涉及到數據流的規劃,通信接口或內部MCU的時鐘約定,工藝條件,功耗等因素。模塊需要明確接口和定義。
在系統級設計上,特別是很多數模混合電路中或對功耗有特別要求的電路中,還要有電壓域的設計,不同模塊之間,功能模塊和接口之間可能都需要根據工藝條件,功耗要求設置不同的電壓。
無論是時鐘,還是電壓,都可以通過控制開關來實現功耗的要求,時鐘實現比較簡單,在大部分電路中都可以實現這種時鐘控制,電壓控制一般是實現在集成有電源管理芯片的較大規模芯片上。但未來趨勢是即使沒有電源管理芯片,電壓的gating也需要納入考慮范圍。
在SOC系統設計上,一個重要的環節是MCU內核的選型,現在常用的內核一般是ARM,較老的ARM7,Arm9等系列,較新的是三大系列cortexA,R,M,具體的用途不做詳細訴述,選定好后核需要根據需要進行設置,一般做硬件的人不需要對它的指令集了解太多,但是需要了解它的總線接口,數據總線,指令總線,以及存儲系統的設計,一般需要安排ROM,RAM分別作為指令和數據存儲器,由于ROM是不可更改的,一般也需要加入flash作為補丁程序寫入地。也可能需要外部存儲器或者DMA控制器來增加外部存儲空間。地址的分配是按照功能需要來進行的,現在有很多工具如synopsys的DesignKits可以產生外部總線代碼及進行地址分配。
第一步完成系統和功能定義后,就要實施的就是RTL實現,RTL是專門描述硬件電路的工具語言,有verilog和VHDL,RTL的特點就是硬件上的同時觸發性,不同于軟件的按順序執行,電路有時序邏輯和組合邏輯組成,時序邏輯在物理構成上就是一些寄存器,這些寄存器受時鐘控制,寄存器代表了電路中數據或控制信號,這些信號受時鐘的驅動流動.組合邏輯是不受時鐘控制的電路塊,組合邏輯顧名思義,通過一些信號的組合直接生成一些邏輯結果。
RTL設計中,一大問題是異步設計問題,異步數據的處理根據不同情況有很多方式,最簡單的,如果對異步的電平信號,可以直接在新的時鐘域中加2級寄存器來隔離,避免不定態的發生.當如果對于總線的處理,或者脈沖的處理,則需要同步模塊,同步模快一般是指需要握手信號,就是前一級時鐘告訴采樣的時鐘,信號ok了,采樣的第二個時鐘再去采,采好后再告訴前一級時鐘,我搞定了,那樣前一級時鐘就可以換數據或其他處理。有一種情況就是前一級時鐘太快,造成第二級來不及,則需要加入FIFO作為隔離,就是讓那些數據先放好,我在慢慢來取.這個FIFO的設計涉及到讀寫地址的判斷,寫滿或讀空都需要做相應處理,讀寫地址之間的判斷只能在其中一個時鐘域中進行,這本身又涉及異步信號的處理問題,這一般用格雷瑪解決,或者有些地方直接可以判斷地址高位,這些方法的目的就是不能讓地址在比較的時候不穩定。
RTL設計中時鐘本身的設計問題也要注意,我們在一個芯片中,盡量把時鐘產生電路放在一塊,主要是從綜合,DFT的角度去考慮的,讓這些時鐘統一管理和約束。時鐘的分頻,切換也要專門處理,否則容易產生毛刺等事情。
RTL設計中還有很多需要注意的問題,比如可綜合性,還有要考慮到電路的面積,以及響應速度等等,這些問題是RTLcoding的基礎問題。
代碼寫完后,需要進行的是驗證工作,下面談談這方面的事情:
上次寫完后,有人支持,也有個朋友說寫這些東西意義不大吧,如果就某些細節方面感興趣的朋友可以探討和切磋,互相學習,也可以私下交流。
這只是我的角度去寫這些東西,希望大家指正和補充。下面接前天的,繼續驗證部分。
芯片驗證一般有這幾個層面,一個是RTL級或者Netlist(preorpostPRwithSDF),這個也是一般意義上的芯片驗證工作,一個是FPGA級的,也是RTL,只不過download到FPGA中,借助硬件環境,也可以直接做應用實驗。
芯片驗證的工作量在芯片設計中占據了大部分的時間和精力,無論是那種驗證,都需要搭建測試平臺(testbench),驗證平臺從軟件結構上模擬芯片的工作環境。即有清晰的連線結構,也有完成這些測試所需要的非結構性的函數或任務包。測試平臺中的被測試芯片是RTL級的,測試向量或者說施加的激勵可以是verilog/VHDL,HDL語言本身就具有比較完善的行為級描述功能,也可以滿足絕大部分測試平臺的搭建和測試激勵的產生,當然我們面對更復雜設計,或追求更高效率也可以使用其它被編譯器兼容的語言,如C/C++,SC,SV,E等等。
很顯然,測試激勵是有時間概念的,是按順序進入和流出芯片的,使用的這些非電路描述語言和功能和軟件幾乎是沒有區別的,所以驗證中也越來越多地使用軟件的一些技術,如面向對象的編程技術,SystemVerilog,SpecmanE等,SV也支持斷言語句(assertion),不同廠家提供的OVM,VMM,UVM等也包括了很多類庫可供使用。關于這些技術其實可以有更深入的探討,也期待感興趣的能深入展開。無論傳統的驗證還是最新的驗證方法學,都需要追求驗證的收斂性,即驗證完全是自動化的檢測,除非debug,我們無需通過波形判斷測試通過與否。對于很多驗證,我們幾乎不需要上到FPGA上驗證,比如數據通信類,完全可以軟件實現數據的產生和比對。而有些應用,如影視頻,圖形抓取等等,如果進入FPGA就可以實現效果功能的檢驗,FPGA的流程這里不做表述,不過要注意的是,我們用作流片的RTL代碼可能和待燒入FPGA的代碼有不同之處,比如有些使用的IP在FPGA中可能和流片廠家提供的不一樣,還有些端口等等需要特別注意。
還有postlayout的后仿,這個后仿是指DFT和APR之后的網表,加入帶有時序信息的SDF文件進行仿真。有些人說,我做過驗證,代碼和網表之間,PR前后網表之間的一致性驗證也做了,還需要后仿嗎?答案當然還是需要的,因為一致性也無法檢測到很多時序的問題,比如毛刺,甚至DFT的錯誤,功能性的問題等等。
現在比較常見數模混合芯片的驗證,模擬的部分主要是采用了verilogams建模,當然也有VHDLRN建模等等,這些東西就是引入了可控制和可檢測的模擬量,進入數字仿真系統,也是數字驗證流程的一環。更專業的數模混合驗證系統ADMS,里面引入了數字和模擬多個引擎,如數字的nvverilog模擬的Eldo等等,數字部分導入RTL代碼,模擬部分直接導入GDS電路,當然為了加快速度,模擬部分在使用中一般仍然導入數字模型。
SOC驗證中,植入了燒入ROM的軟件,在仿真驗證系統中,使用的一般是存儲器的模型加上文本格式的代碼文件,一般實現是直接通過系統讀入指令把文件讀入到存儲器模型中。(有些仿真工具可以直接通過選項導入,類似SDF文件,如工具nscim),仿真器可以直接寫出指令執行的log以用于debug,不過現在有更先進的方式用于復雜SOC驗證,如Codelink工具,能夠在原仿真器的基礎上,建立起MCU和HDL電路已經軟件的關系,通過展示波形和固件(firmware)源碼的鏈接進行更方便的debug。下面需要說的實現部分,就是綜合,DFT,STA,ATPG等,準備合在一起寫,感興趣的朋友也可以接著我的寫,我暫時不做這方面的更新,希望朋友們玩個接力。
(此處略去若干回帖。。。。。)
帖子的話題受到了大家關注,也被加精,受到鼓舞,一直想繼續寫完,但時間有限,更主要惰性使然,一直沒有接著寫完,今日得寬裕,繼續談談數字IC設計接下來的一些工作。不過在繼續之前,我要說的是,其實我之前寫的也不夠完善,只是想到了就隨之敲下來了,不夠完整,特別是現在的芯片設計,不同的產品,要求不同,所設計的步驟也不完全一致。比如,對于很多設計數字電路可能實現建立軟件模型,來評估我們的頻率,資源,或者數字信號處理中的信噪比等一些指標,這些都是在規格定義之后,電路設計之前需要做的一些工作。在芯片完成了設計與驗證之后,我們要做的工作就是把它裝化成GDS網表,就是所謂的implementation(電路實現),這其中的流程包括綜合,DFT,formality,STA,ATPGpatterngeneration和仿真(和功能驗證一樣也包括前后仿),再進入后端的PR/LVS/DRC的流程,這個屬于物理設計,當然這個流程是籠統的,這里主要講幾個后端物理設計之前的前端實現的幾個步驟。綜合的原理比較直觀,從RTL到GTECH庫到廠家工藝庫的編譯翻譯映射與替代,綜合需要輸入的就是時鐘約束即sdc文件,sdc是設計與產品的使用要求決定的。DFT包括好幾種類型,Scanchain是最常見的一種,這個可以在綜合步驟中一起做,也可以在綜合之后進行,掃描鏈的多少和芯片本身的規模和芯片可提供的可測試芯片管腳有關,盡量掃描鏈的長度越短越好,以縮短測試時間;由于有些片子的管腳有限,需要壓縮掃描鏈;甚至有些芯片由于可復用的管腳太少,進入測試模式都需要在功能模式下由主機的設定,設定后再退出功能模式。掃描鏈的插入后可以由工具產生ATPG的pattern,DFT成功與否要看測試覆蓋率,一般在95%以上說明就差不多ok了,如果覆蓋率偏低,需要追究原因,一般看有沒有漏掉的DFF,以及為什么會溜掉,一般主要原因不是這個,是一些不可控的節點造成了覆蓋率低,需要在電路中做一些處理來使之能有相關ATPG產生,如加入測試模式生效的可選的DFF或者電平/地。有些電路包括了ROM/RAM,需要給ROM/RAM本身加入內建自測電路(BIST),一般如果帶有MCU的SOC,其實也可以通過軟件來完成RAM測試,不一定加入BIST,但是植入CPU軟件的ROM肯定必須有BIST電路來完成自測,自測電路的原理很簡單,就是判斷寫入和讀出的數據是否吻合,否則會BIST電路會給出錯誤的標識。Boundaryscanchain是另一種DFT,很容易理解的是,我們可以控制芯片各個pad的輸入輸出值,這樣它的作用可以應用于板級調試。formaliry或者說equencecheck,主要檢查綜合,DFT以及后端PR之后和各自步驟之前的RTL/網表的等效性。STA是靜態時序分析,主要用在芯片完成后端流程后,也檢查是否滿足時序要求,特別是插入了時鐘樹之后,這是需要后端工具(ICC/Encounter等)反標出的sdf文件來進行分析。細節不表。STA完成后才可以進行功能后仿和ATPGpattern的后仿。
異步電路中時鐘如何同步的多種方法
第一種情況:
當有多個時鐘在同一個數字電路中,且有一個時鐘(Clk)的速率大于其它時鐘兩倍以上。
這種情況最為簡單,在接口部分就必須要對其他時鐘進行同步化處理,將其處理為與Clk同步的時鐘信號。
這樣處理的好處是:
便于處理電路內部時序;
時鐘間邊界條件只在接口部分電路進行處理。
實質上,時鐘采樣的同步處理方法就是上升沿提取電路,經過上升沿提取輸出信息中,帶有了系統時鐘的信息,所以有利于保障電路的可靠性和可移植性。
第二種情況:
當系統中所有時鐘沒有一個時鐘速率達到其他時鐘頻率的兩倍的情況,也就是系統中多個時鐘速率差不多的情況。
這個時候無法滿足采樣定理,所以在接口部分就必須對其他時鐘和數據通過FIFO或者DPRAM進行隔離,并將其他時鐘信息轉換為和系統時鐘同步的允許信號。比如在高速的數據采集系統當中,AD的采集時鐘往往比較高,大于系統時鐘的一半以上,這時候采用同步化處理無法滿足時序設計。
第三種情況:
系統中多個時鐘之間存在數據互相采樣。
對于這種情況,可使用兩級觸發器級聯采樣數據,避免亞穩態發生。
第四種情況:
多級時鐘網絡處理。
所謂多級時鐘網絡是指時鐘經過超過一級的門電路后連到觸發器的時鐘輸入端。
由于時鐘建立-保持時間的限制,FPGA設計中應盡量避免采用多時鐘網絡,在設計中必須要將時鐘網絡進行簡化,盡量采用使能的方式或者其他簡化的電路結構。
好了,文章到這里就結束啦,如果本次分享的異步fifo的設計和設計同步fifo,每100個cycle問題對您有所幫助,還望關注下本站哦!