Saturday, 28 January 2012

ARM memory management (Chip S3C2440 with Core ARM920T)


  1. 128MB for every bank.
  2. Total 8 memory banks.Six memory banks for ROM, SRAM, etc.Remaining two memory banks for ROM, SRAM, SDRAM, etc .
  3. There are 7 banks with fixed start address.
  4. The size and start address of bank7 can change, while the size of bank6 is changeable (equal to the size of bank7), but with fixed start address.

The memory mapping is shown below:

From picture above, we can see:
  1. Although S3C2440 is a 32-bit chip which theoretically means the chip can have a maximum memroy 4GB, but in fact it only has 1GB memory to use for data and code. As for the remaining 3GB, some of which is used for special registers, and the others is not used (it is kind of waste, isn't it!).
  2. The start address and size of bank7 is changeable, but the sizes of bank6 and bank7 have to keep the same. So the size of bank6 is also changeable, but with fixed start address.
  3. Usually, the bank6 and bank7 are used for SDRAM, so basically bank6 and bank7 is used for RAM. So the maximun of RAM of S3C2440 chip is 256MB.

Memory organization with S3C2440 application:

I will talk about memory organization according to board TQ2440 which is bought from China. The specification of core board is:
  • S3C2440 CPU
  • 64MB SDRAM
  • 2MB Nor flash
  • 512MB Nand flash
There are three kinds of memory chip can be used with S3C2440: SDRAMNor flash and Nand flash. We can choose Nor flash or Nand flash as the working ROM though pins OM1 and OM2 like the table below.


S3C2440 chip uses address pins LADDR0-LADDR26 (128MB) and banks selection pins nGCS0-nGCS7 to locate the certain memory units. So when the chips try to decode the PC register, there are two decoding modes:
ROM: nGCS pin + address pins
RAM: nGCS pin + address pins (Multplexing)

SDRAM connections:

Here we talk about SDRAM first, which uses multiplexing addressing mode to access memory units. Two 32MB 16-bit width SDRAM chips is used together to form 64MB RAM which has 32-bit data width


From the schematic above, we can see:
  1. The address pins are multiplexing pins: A0-A12 for rowsA0-A8 for columns.
  2. BA0-BA1 are used for selecting the internal banks of SDRAM.
  3. The memory system is 4-byte alignment. So the address pins are used from LADDR2, LADDR0 and LADDR1 are not used.
  4. nSCS is the chip selection pin connected with nGCS6 pin of S3C2440, which means SDRAM working in address space 0x30000000.
Here we can calculate how we get the 64MB RAM like below:
2^(13(A0-A12)+9(A0-A8)+2(BA0,BA1))*16 bit*2 chip = 64MB

Nor flash connections:

Nor flash is an XIP (executed in place) memory. It uses linear addressing mode which make the addressing is the easiest.

From the schematic above, we can see:
  1. There are 20 address pins (A20 and A21 are not used).
  2. The data width is 16-bit, so the address pins are used from LADDR1Does this mean we have to only store the THUMB instructions which are 16-bit size in this kind of Nor flash?
  3. Here the chip selection pin is connected to the pin nGCS0 of S3C2440 chip, which means it will work in address space 0x00000000.
The 2MB size of Nor flash can be calculated like below:
2^20(A0-A19)*16bit = 2MB

Internal SRAM:

S3C2440 chip has its own internal SRAM whose size is 4KB. This is far enough from storing the kernel of operating system and it is volatile. So we have to use externel memories.
When we use Nand flash mode (OM(1,0) = 00), the 4KB SRAM is mapped to address 0x00000000, now it is called "Steppingstone", so that it can load the boot program.
When using Nor flash mode (OM(1,0) = 10 or 01), the SRAM mapping is changed into address 0x40000000, because Nor flash is mapped to 0x00000000 to boot program in Nor flash.

Anyway, when the system resets, the program is always executed from address 0x00000000.

Summary:

The memory location of board TQ2440 is:

0x00000000->0x001FFFFF                                                         2M Nor flash
0x30000000->0x33FFFFFF                                                         64M SDRAM
0x00000000->0x00001000 or 0x40000000->0x40001000          4K Internal SRAM
0x48000000~                                                                                Special registers

As for Nand flash, which can be regarded as the normal peripherals whithout mapping in address space of S3C2440. So here Steppingstone is coming. When the system resets, the first 4KB in Nand flash is carried into the Steppingstone by Nand flash controller. The 4KB bootloader program loads the kernel of operating system into the SDRAM which is mapped to 0x30000000.
We can see the design of Steppingstone in S3C2440 is sweet. Without Steppingstone, we have to use an extra Nor flash to store the bootloader code which can load the kernel image of operating system. Now with Steppingstone, we can save both bootloader code and kernel image in Nand flash.

References:
[1] S3C2440 datasheet, Sumsung
[2] S3C2440存储控制器和MMU浅析, http://blog.csdn.net/ayangke/article/details/6880846
[3] TQ2440学习笔记-裸奔程序流水灯, http://www.dpj365.com/blog/?tag=%E7%AC%94%E8%AE%B0


Verilog杂七杂八


[1]"A module can contain any combination of following: net/variable declarations(wire, reg, integer, etc.)concurrent and sequential statement blocks, and instances of other modules(subhierarchies)Sequential statements are placed inside a begin/end block and executed in sequential order within the block. But the blocks themselves are executed concurrently, qualifying Verilog as a data language."

[2]"There is no need to explicitly declare wires; any undeclared identifiers default to wires. Note: undeclared wires must by single bit."

"All continuous assignments are active concurrently."

"In Verilog we use procedural blocks to model edge-triggered behaviour."

"An always block can be used to describe both combinational and sequential hardwire."

"All blocks in the design execute concurrently."

[5] "assign声明只能用在组合逻辑电路(combinational logic)中,不能用在always block中,即时序逻辑电路中(sequential logic)。"

reg vs wire:

[2]
reg signals can be
  • connected to the input port of a module instance
  • used as outputs in a module declaration
  • assigned to in an always @ block using <= or =
  • assigned to in an intial block (later)
  • used to create registers in an always @ (posedge clk) block
  • used for both sequential and combinational logic
wire signals can be
  • used to connect inputs and outputs of module instances
  • used as inputs and outputs in module declaration
  • can be assigned to with an assign statement
  • used to model only combinational logic
reg signals cannot be
  • connected to the output ports of a module instance
  • used as inputs in a module declaration
  • used on the left side of an assign statement
wire signals cannot be
  • used on the left side of a <= or = assignment in an always block
  • used to model sequential logic

     从上面的资料我们可以看出,wire一般用于blocks外面的线连接仅仅是用来连接的,所以其没有记忆功能。而reg一般使用在blocks中像变量一样使用(也可以在blocks外面使用),配合着<=,可以实现状态间的数据传输,实现系统的记忆性。例如:
     always @ (posedge clk)
     begin
          q1<=in; 
          q2<=q1;
          out<=q2;
     end
     两个clock状态,前一个clock的数据能保留到当前clock的原因就是因为in, q1, q2都是reg类型。

     下面是一段来自百度百科对wire和reg不同较为准确地讲解:
     [4]"Verilog里一般不声明输出类型的话  默认是wire型的如果你想在输出处寄存一下:比如使用always语句,则必须声明为reg类型。wire是线网,就是相当于实际中的连接线你想assign的话就是直接连接,就是用wire型,它的值是随时变化的。比如你想寄存一下,让他在时钟边沿才变化就需要reg类型了"

begin/end and fork/join

begin/end块内的语句都是顺序执行的,就像软件程序一样儿,当然要除去使用nonblocking "<="的情况。nonblocking的部分先同时计算"<="右边的部分,然后在遇到end的时候同时放到"<="左边的reg中。[6]

begin/end和fork/join可以互相嵌套。

Blocking and Nonblocking

Nonblocking "<=" :

"<=" 右边所有的表达式先计算,直到所有的计算都完成后(遇到end from "begin/end"),才将右边所得的所有计算结果assign到左边的register中。
用在sequential logic中
实际上主要用于Flip-Flop circuit

Blocking "=":

"=" 计算完立即放入左边,每一个式子都是这样。
用在combinational logic中


Combinational logic(组合逻辑电路):

即,有输入就立即产生当前状态下的输出没有任何"记忆性"
Sequential logic(时序逻辑电路):

即,输出与过去的输入有关,所以系统具有"记忆"功能







References:
[1] "Verilog Wikipedia", http://en.wikipedia.org/wiki/Verilog
[2] "2 - IntroVerilog Lecture", NTU embedded systems lectures
[3] "Blocking vs Nonblocking Assignments.pdf", in Dropbox
[4] "verilog变量reg和wire问题", 百度百科 http://zhidao.baidu.com/question/262629908.html?fr=qrl&cid=866&index=3
[5] "Verilog中的assign以及always", 百度百科 http://zhidao.baidu.com/question/358599023.html
[6] Procedual blocks: begin-end and fork-join http://electrosofts.com/verilog/beginend.html

Friday, 27 January 2012

Package on package design


Package on package design
Package on package (PoP) is an integrated circuit packaging method to combine vertically discrete logic (like processors) and memory ball grid array (BGA) packages which is used in A5 chip by iphone.There is a standard interface to route signals between different packages. It is shown as following:

Two widely used configurations exist for PoP:
1. Pure memory stacking - two or more memory only packages are stacked on each other
2. Mixed logic-memory stacking - logic (CPU) package on the bottom, memory package on top. For example, the bottom could be an application processor for a mobile phone. The logic package (processors) is on the bottom because it needs many more BGA connections to the motherboard.

Benifits:
  • 1), Mother space saving
  • Minizing track length between different interoperating parts, such as a controller and a memory.
1), faster signal propagation
2), reduced noise and cross-talk
  • Memory is decompled from logic device.
   1), The memory package can be tested separately from the logic package
   2), Only "known good" packages are used in final assembly (if the memory is bad only the memory is thrown away and so on).
   3), The end user (such as makers of mobile phones or digital cameras) controls the logistics.
   4), Because the construction is like Lego (乐高玩具), any mechanically mating top package can be used.
   5), Because the memory only comes into the mix at final assembly, there is no reason for logic suppliers to source any memory. With a
       stacked-die device, the logic provider must buy wafers of memory from a memory supplier.

Reference : http://en.wikipedia.org/wiki/Package_on_package Acessed: Nov 14, 2011.




Byte Alignment


Byte Alignment, actually is designed for realizing the convenience of memory addressing, to improve the speed of addressing. To understand it, we gonna talk about the difference between using Byte Alignment and non-Byte Alignment:

struct A { 
    char c; 
    int i; 
}; 
struct A a;

We use the structure above to explain the function of Byte Alignment. Without Byte Alignemnt, the address of variable c is 0x00, and the address of i is 0x01. If the addressing scope  of cpu is 4 bytes, we will need two read cycles to get i. Variable c need a read cycle to be read, however variable i need two read cycle to be read because there are three bytes of i in 0x01, 0x02, 0x03 which are read in the variable c read cycle. So when the cpu read the 0x04, it has to connect the three bytes 0x01, 0x02, 0x03. As we see, we need two read bytes to get i, it is low efficient.
However, if we set the byte alignment=4, problem will be solved, because by doing this, we make 0x01, 0x02, 0x03 in vain and set 0x04 as the address of variable i. And variable i can be read for just one cycle. Although three bytes have been wasted, we improve the efficience of addressing.

Thursday, 26 January 2012


Synchronous and Asynchronous(in hardware communication)

Synchronous: different hardwares must use the same clock signal. For example, SPI protocol require the Master and Slave have to use the same clock signal which comes from Master.
Asynchronous: different hardwares can use different clock signal( the baudrate should be the same). For example, UART transmits data with 8 bits which contains start bit and/or stop bit used to verify if the data transmit is correct because of the different clocks they use. Please refer to the artile on 1st April 2011.

Serial Interfaces
SPI(Serial Peripheral Interface) is a serial interface standard. Basically, SPI need four pins which are: cs, clk, so and si. And most of SPI use Master-Slave mode to work.

cs: used to select the Slave chip. There is nothing to say about this part.
so: used to output bit stream. It works with clk pin to output bit at the positive edge of clk signal.
si: used to get bit stream. It also works with clk pin to get bit at the negtive edge of clk signal.
clk: used to provide synchronous signal which is created by the master chip.

So SPI can send and receive data at the same clk.
Note: Remember what counters is mechanism, maybe different companies have different implementation (maybe capture send at negtive edge of clk, receive data at positive edge of clk). Another thing you need to notice is that SPI is a protocol.


Sunday, 22 January 2012

Make ARM program using IAR Embedded Workbench IDE


Build a project: Project -> Create New Project.
After create a project, you can programm C code in file window. When you finish programming, you need to configure the project first.
Open project option: Project -> Options.
In the General Options category, you can configure the type of Processor Core or Chip Device. And click the third tab Code under  sub category C/C++ Compiler, you can set the Processor mode: Arm or Thumb.

In the Debugger category, you can set the Diver for SimulatorJ-LinkJ-TraceThird-Party Driver or something else.
If the Driver above, you choose Third-Party Driver, then in Debugger -> Third-Party Driver, you can choose the path of the Third-Party Driver.

After the configuration, you can compile and make the C code. Then click the button "Download and Debug", must beware not "Debug without Downloading".
Once entering Debug mode, you can see RegisterMemory, and the most important item Disassembly in the View option. (Here beware again that before entering Debug mode, you cannot click button "Debug without Downloading", it has to be "Download and Debug").

Startup in linux


Startup in linux:
1, In linux, file /etc/init.d/rc.local is the startup file of linux. So if you want to start some commend when the system boot, you can write the commend or script at the end of the file.
2, If you want to mount some disk, first you need to be sure the disk wasn't mounted on some place else. Otherwise, you need to unmount the dev file before the mount the file on the target.

Lex 与 Yacc


Lex 与 Yacc

     其实,在我看来lex和yacc就是一套文字识别及语法分析的编程软件,它们基于宿主语言(host languge)进行编程,例如常用的宿主语言有CC++basic。但是它们在自己的规则部分(也就是文件的第二部分)都有相应的语法规则,例如lex用正则表达式(regular expression)实现词法识别,词法识别实际上就是把输入文件中字符进行归类(例如关键字变量立即数等),将其归类为不同的tokens。这里还涉及到一个概念那就是terminals。在这里terminals和tokens是同义词,表达的是一个含义,都是不能再分割的基本单元。这里不能再分割指的就是区别于yacc中可以再分割的nonterminals因为yacc中规则部分实际上就是利用递归(recursion)来进行语法识别yacc用nonterminals不断地递归下一层的nonterminals或者terminals,直到所有的最底层的语法都用terminals来表述,即不能再分割terminals大写字母表示,nonterminals小写字母表示。这里先做简单介绍,后面会有详细的讲解。

1.如何编译和执行Lex与Yacc源代码?

     例如有lex和yacc的源代码分别为example.l和example.y。如果宿主语言是C语言的话(以下未特殊说明,均指C语言),由于lex和yacc的源代码规则部分都不是C语言,所以不能直接拿来用GCC编译,所以我们要先用lex和yacc指令来将源代码转换成完整的C语言文件。如下:
     lex example.l
     yacc -d example.y
     第一条指令执行后生成lex.yy.c的C程序文件。第二条指令因为使用了option -d,所以该条指令生成两个文件,分别是y.tab.c和y.tab.h。这里y.tab.h里面包含的是各个token的宏定义,从257开始给每个token定义一个一个数字。所以通常该文件要使用#include<y.tab.h>包含在lex的源代码中,本例中,即example.l文件内,以便在后面的编译中使用。接下来就是编译生成的C文件,从而生成可执行文件。如下:
     cc lex.yy.c y.tab.c -o example
     执行可执行文件./example就可以实现编译器了。
总结:
     编译lex、yacc源代码指令序列
     lex xxx.l                                //生成lex.yy.c
     yacc -d xxx.y                        //生成y.tab.c和y.tab.h
     cc lex.yy.c y.tab.c -o xxx       //生成可执行文件xxx
     Lex&Yacc的结构图如图1所示:

图1 Lex&Yacc的结构图[1]
2. Lex的使用规则:

     lex实际上就是一个文字识别软件,通过扫描一串输入字符来识别出你需要的部分,从而做出相应动作,即pattern/actions模式。简单的编译器完全使用lex就可以做到,但复杂的结构就不得不用yacc来作语法分析。

2.1 Lex源文件结构:

     正如上面提到的,Lex的源代码分为三个部分:声明部分(头文件包含、C宏定义、lex声明)、Lex规则部分、以及C语言部分。三部分之间用%%符号来分隔,只有第二部分是必须的,其它部分可以省略。完整的Lex代码如下所示[1]

       %{
       C declarations and includes
       %}
          Lex macro definitions and directives
       %%
          Lex Specification
          in the form of pattern/action statements like this:
          keyword    { my_c_code(yytext); }
       %%
          C language program (the rest)

     其实第一部分和第三部分可以合并成一个部分,对整个程序不会有什么影响。

2.2 Lex声明以及start conditions:

     在lex的声明部分,%{%}之间是头文件包含以及宏定义等用C语言描述的部分。%}之后则是lex声明,这里一般用来声明start conditions[2]
     这里我们重点讨论一下start conditions。我们可以在lex的声明部分声明一些start condition symbols,这些symbols用来控制在什么时候来匹配什么字符,或者不匹配什么字符。我们可以随时更改start condtion,只要在规则的action中使用指令BEGIN symbol;,如果想返回到原始的状态,可以使用指令BEGIN 0;或者BEGIN INITIAL;。在规则部分我们用<symbol>来标记处于某状态的 pattern/actions。Start conditions有两种模式一个是Inclusive condition,另一个是exclusive condition
     Inclusive condition用%s来声明,如果该时刻处于某个inclusive状态,则所有该inclusive状态的<symbol>normal状态下的<symbol>指定的pattern/actions都会得到执行。
     Exclusive condition用%x来声明。当遇到处于exclusive状态的<symbol>时,只有该symbol下的pattern/actions能得到执行,而normal状态<symbol>下的pattern/actions都不会执行
     举例如下[2]
     %s          one
     %x          two
     %%
     abc                    {printf("matched "); ECHO; BEGIN one;}
     <one>def           {printf("matched "); ECHO; BEGIN two;}
     <two>ghi           {printf("matched "); ECHO; BEGIN INITIAL;}
     在这个例子中,如果处于one状态,则abc和def都会被检测匹配。然而处于two状态,则只有ghi会被检测匹配

2.3 Lex的规则部分:

     其实在这部分唱主角的应该是正则表达式,但是要想说清楚它,要另写一篇文章了。在这里主要是介绍Lex+Yacc,所以正则表达式就直接忽略。
     简单地讲,这部分规则就是pattern/actions,如下:
     regular_expression          { C-program statements }
     在{ C-program statements }部分你可以写任何C code。但是在与yacc合用的时候一般都是返回terminals数值,例如yylval=atoi(yytext); return NUMBER; }
     说到这里,我要先介绍几个lex常用的内部变量,正如上面的yytext(yylval是Yacc中的内部变量)。如下表[6]:
     
yyin        FILE* 类型。它指向lexer正在解析的当前文件
yyout      FILE* 类型。它指向记录lexer输出的位置。缺省情况下,yyin和yyout都指向标准输入和输出。
yytext匹配模式的文本存储在这一变量中(char*)。
yyleng     给出匹配模式的长度。
yylineno提供当前的行数信息。(lexer不一定支持。)

2.4 Lex的C语言部分:

     如果不用分析任何输入文件的话,直接从标准输入读取字符,则只需要在main函数中执行yylex()即可。但大多数情况下,需要我们从一个文件中读取字符。所以一个标准的读取文件C语言程序如下[5]:
     
%{
#include <stdio.h>
#include <errno.h>
int file_num;
int file_num_max;
char **files;
extern int errno;
%}
%%
(ftp|http):\/\/[^ \n<>"]*          printf("%s\n",yytext);          //打印输出网址
.|\n                                   ;                                            //消除任意单字符及换行符
%%
int main(int argc, char *argv[])                                       //这段代码就是常用来读取输入文件的,当执行yacc可执行文件的时候,可以附带多个参数在数组argv[]中
{
     file_num=1;
     file_num_max=argc;
     files=argv;                                                                //这句代码很重要,把参数的首地址赋给files,以便后面的yywrap()函数使用。
     if(argc>1)
     {
          if((yyin=fopen(argv[file_num],"r"))==0)
          {
               perror(argv[file_num]);
               exit(1);
          }
     }
     while(yylex())     ;
     return 0;
}
int yywrap()                                                                 //这个函数用来连续读取多个文件,当该函数返回1时,整个读取程序结束。所以需要我们手动给yywrap()函数返回1
{
     fclose(yyin);
     if(++file_num<file_num_max)
     {
          if((yyin=fopen(files[file_num],"r"))==0)
          {
               perror(files[file_num]);
               exit(1);
          }
          return 0;
     }
     else 
     {
          return 1;
     }
}

     以上是一个通用的字符读取程序。如果再想进一步化简一下,当Yacc的可执行文件的参数只有一个的时候,yywrap()函数可以简化如下:

int yywrap()
{
     return 1;
}

     下面介绍几个常用的Lex函数[6]:

yylex()                          从这一函数开始分析。它由Lex自动生成
yywrap()
                   
这一函数在文件(或输入)的末尾调用。如果函数的返回值是1,就停止解析。因此它可以用来解析多个文件。代码可以写在第三段,这样就能够解析多个文件。方法是使用yyin文件指针指向不同的文件,直到所有的文件都被解析。最后,yywrap()可以返回1来表示解析结束。
yyless(int n)                                                                  这一函数可以用来送回除了前n个字符外的所有读出标记。
yymore()这一函数告诉Lexer将下一个标记附加到当前标记后。

     yylex()函数是整个Lex源代码的精华,非C语言部分(包括lex声明部分,规则部分等)通过Lex自动编译成C代码的一个子函数yylex()。因此在主函数main()中调用yylex()就可以执行整个字符识别程序。

2.4 总结:

     通过Lex我们可以将一串输入字符,分析出我们想要得到标识。然后再利用Yacc进行语法分析。

3. Yacc的使用规则:

     Yacc使用“分析树”的原理来进行语法分析,这里我们通过递归的原则来实现分析树。

3.1 Yacc的文件结构:

     Yacc的文件结构基本与Lex的源代码结构类似。同样是分成三个部分,只是第一部分通过关键字%token来定义所有的terminals (nonterminals不用定义,直接用symbol:的形式在规则部分使用即可)。
     
3.2 Yacc规则部分:

     变量yylval通常用在lex规则中在变量yytext配合下来接受内容(可能是数字也可能是字符串等)。
     而在Yacc规则部分,用$number来接收数值等。
     在标记都是terminals的时候,我们直接用$number来获取数值即可。例如:

expression :      NUMBER '+' NUMBER          {printf("The result is %d\n", $1+$3); } //这里要重点注意的是,我们使用$1+$3,而不是$1+$2,因为'+'也是一个symbol

     如果标记是nonterminals,由于nonterminals不会出现在Lex的源代码中,所以我们要用另一种方法来给nonterminal symbol赋值,即用$$来给当前nonterminal symbol赋值。例如:

%token NAME NUMBER
%%
statement:     NAME '=' expression
          |          expression                    { printf("= %d\n", $1); }
          ;
expression:     expression '+' NUMBER     { $$=$1+$3; }
          |           expression '-' NUMBER      { $$=$1-$3; }
          |           NUMBER                           { $$=$1; }
          ;
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

     上面这段程序写成如下的递归形式更容易理解。从上面的代码可以看出,上面的规则不能用C语言的执行来理解,两者是完全不同的。从某种角度讲,上面的程序含义倒与verilog有几分相似。它们都表示一种,符号与符号之间的关系。没有什么执行顺序概念,理解这一点很重要,否则无法读懂其它程序,就算读懂,也不能写出自己的程序。所以一定要切记一点: 不要用C语言去思考所有的编程语言,很多时候它们的执行规则是完全不同的
     总之,上面这段代码就是描述下面这幅分析树,就像画画一样,没有执行次序与执行次数的概念,就是描述你如何画出这幅树形图的。所以我猜测,大概verilog等HDL也应该是这样儿理解吧。

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
     


3.3 Yacc 的C语言部分:

     这里直接调用yyparse()函数进行语法分析,而不用再单独调用yylex()了,因为在yyparse()函数的实现中已经调用了yylex()。而且lex.yy.c和y.tab.c中只能有一个main()函数一般main()函数放在y.tab.c中

3.4 总结:

     这里没什么好讲的了,文件结构和使用方法都和Lex有很大的相似之处,所以对比Lex来讲解会更容易理解。


Reference: 

[1] Lex - A Lexical Analyzer Generator, http://dinosaur.compilertools.net/lex/
[3] Lex and Yacc primer/HOWTO, http://ds9a.nl/lex-yacc/cvs/lex-yacc-howto.html
[4] System Software -- Compilers, http://www.cs.man.ac.uk/~pjj/cs5031/ho/ho.html


Difference between "docker stop" and "docker kill"

 To stop a running container, you can use either "docker stop" or "docker kill" command to do so. Although it seems doin...