Blog | Tag | Local | Guest | Login | Write |  RSS
2008/11/17에 해당되는 글 2건
2008.11.17 :: ARM7의 메모리 구조 3
Packing Widget _ Box Packing


안녕하세요.
저번시간에는 아주 간단하지만 중요한 이야기를 했었죠? 저번시간에 이야기했던 Packing에 대해서 좀더 자세히 공부해 볼까 합니다. 우리가 어떤 어플리케이션을 개발하면서, 하나의 윈도우에 하나 이상의 버튼을 놓으려 할 것입니다. 우리의 첫번째 hello world 예제는 하나의 widget만 썼었죠. 그러나 우리가 하나의 윈도우에 더많은 widget을 놓으려 할 때, 어떻게 그들이 놓일 위치를 제어해야 할까? 여기서 바로 "packing"이란 것이 등장한죠!!

1. 박스 packing의 원리
대부분의 은 앞서의 예제에서처럼(저번주 강의) 박스를 만드는 것으로 이루어집니다. 이들은 우리의 widget을 수평 혹은 수직 방향으로 패킹해 넣을 수 있는, 보이지 않는 widget 컨테이너들이었죠. 수평 박스로의 패킹 widget인 경우, object는 호출 하는 방식에 따라 수평으로 왼쪽에서 오른쪽으로 혹은 오른쪽에서 왼쪽으로 삽입 됩니다. 수직 박스에서는 반대로 수직으로 삽입됩니다. 우리는 원하는 효과를 내기 위해 다른 박스들의 안팎에서 어떻게라도 조합해서 사용할 수 있겠죠?
새로운 수평박스를 만들기 위해 우리는 gtk_hbox_new()를, 그리고 수직박스를 위해서는 gtk_vbox_new()를 이용합니다. gtk_box_pack_start()와 gtk_box_pack_end ()는 이런 컨테이너의 내부에 object들을 위치시키기 위해 사용합니다. gtk_box_ pack_start()함수는 수직박스에서는 위에서 아래쪽으로, 그리고 수평박스에서는 왼쪽에서 오른쪽으로 패킹할 것입니다. 그리고 gtk_box_pack_end()는 이와 반대 방향으로 패킹합니다. 이 함수들을 이용함으로써 우리는 오른쪽 또는 왼쪽으로 widget을 정렬할 수 있고, 원하는 효과를 낼 수 있습니다. 우리는 대부분의 예제에서 gtk_box_pack_start()를 이용할 것입니다. Object는 또다른 컨테이너이거나 widget이 될 수 있습니다. 그리고 사실, 많은 widget들은 실제로 버튼을 포함하고 있는 widget 이지만, 우리는 보통 버튼 안의 라벨만을 이용할 것입니다.
이런 함수호출로써, GTK는 우리가 widget을 놓을 위치를 알게되고 따라서 자동적으로 크기를 조절한다든지 또다른 매력적인 일들을 할 수 있게 됩니다. 또한 우리의 widget이 어떻게 패킹되어야 하느냐에 따른 수많은 옵션들도 있습니다. 우리가 상상하듯이, 이런 방식은 widget을 놓고 만드는 데 있어서 상당한 유연성 을 제공해 줍니다. 매우 기특한 녀석이죠? ㅋㅋ 

2. Box에 대해서 좀 더 알아볼까요?
이런 유연성 때문에, GTK에서 박스를 패킹하는 것은 처음엔 혼란스러울지 모릅니다. 많은 옵션들이 있으며, 그들이 어떻게 서로 꿰어 맞춰지는지 바로 알 수는 없을 것입니다. 그러나 결국, 우리는 다섯 가지의 기본적인 스타일을 가지게 됩니다. 


각의 줄은 몇 개의 버튼을 가지고 있는 하나의 수평박스(hbox)를 포함합니다. 함수호출 gtk_box_pack은 이 수평박스에 각각의 버튼을 패킹하는 것을 단축한 것입니다. 각각의 버튼은 이 수평박스에 같은 방법으로 패킹됩니다.
이것은 gtk_box_pack_start함수의 선언이다.

void gtk_box_pack_start (GtkBox    *box,
                         GtkWidget *child,
                         gint       expand,
                         gint       fill,
                         gint       padding);

첫번째 인자는 object를 패킹할 박스고 두번째는 그 object입니다. Object는 여기서 모두 버튼이 될 것이고, 따라서 우리는 박스안에 버튼들을 패킹하게 됩니다.
gtk_box_pack_start() 또는 gtk_box_pack_end()에서의 expand라는 인자가 TRUE 일 때, widget은 여백공간을 가득 채우며 박스에 들어가게 될 것입니다. 그리고 그것이 FALSE라면 widget은 적절히 여백을 두게 된다. 이 expand를 FALSE로 두면 우리는widget의 좌우 정렬을 결정할 수 있습니다. 그렇지 않으면 그들은 박스에 가득차서 gtk_box_pack_start 또는 gtk_box_pack_end 어느 쪽을 이용하든지 같은 효과를 가지게 됩니다.
인자 fill은 TRUE일 때 object 자신의 여백공간을 제어합니다. 그리고 FALSE라면 object 자신의 여백공간을 두지 않습니다. 이것은 expand 인자가 TRUE일 때만 효과가 있어요.

새로운 수평박스를 만들 때는 이런 함수가 있습니다.
GtkWidget * gtk_hbox_new (gint homogeneous,
                          gint spacing);

여기서의 인자 homogeneous는 박스 안의 각 object들이 같은 크기를 가지도록 제어합니다. 즉 수평박스일 경우엔 같은 너비, 수직박스일 경우엔 같은 높이이겠죠? 이것이 세팅되면, gtk_box_pack함수의 expand 인자는 언제나 TRUE가 됩니다.

3. 패킹에 대한 예제 프로그램
/* packbox.c */

#include "gtk/gtk.h"

void
delete_event (GtkWidget *widget, GdkEvent *event, gpointer data)
{
  gtk_main_quit ();
}

/* Button_label들로 이루어진 hbox를 만듭니다.  */
GtkWidget *make_box (gint homogeneous, gint spacing,
                     gint expand, gint fill, gint padding)
{
  GtkWidget *box;
  GtkWidget *button;
  char padstr[80];

  /* 적당한 homogenous와 spacing을 가진 hbox를 만듭니다. */
  box = gtk_hbox_new (homogeneous, spacing);

  /* 적절히 세팅된 버튼들을 만듭니다. */
  button = gtk_button_new_with_label ("gtk_box_pack");
  gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
  gtk_widget_show (button);

  button = gtk_button_new_with_label ("(box,");
  gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
  gtk_widget_show (button);

  button = gtk_button_new_with_label ("button,");
  gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
  gtk_widget_show (button);

  /* expand의 값에 따르는 라벨을 가진 한 버튼을 만듭니다. */
  if (expand == TRUE)
    button = gtk_button_new_with_label ("TRUE,");
  else
    button = gtk_button_new_with_label ("FALSE,");

  gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
  gtk_widget_show (button);

    button = gtk_button_new_with_label (fill ? "TRUE," : "FALSE,");
  gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
  gtk_widget_show (button);

  sprintf (padstr, "%d);", padding);

  button = gtk_button_new_with_label (padstr);
  gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
  gtk_widget_show (button);

  return box;
}

int
main (int argc, char *argv[])
{
  GtkWidget *window;
  GtkWidget *button;
  GtkWidget *box1;
  GtkWidget *box2;
  GtkWidget *separator;
  GtkWidget *label;
  GtkWidget *quitbox;
  int which;

  gtk_init (&argc, &argv);

  if (argc != 2) {
    fprintf (stderr, "usage: packbox num, where num is 1, 2, 3.\n");

    /* GTK를 끝내는 부분이며, exit status는 1입니다. */
    gtk_exit (1);
  }

  which = atoi (argv[1]);

    /* 윈도우를 만듭니다. */
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

  /* Main 윈도에 destroy 시그널을 연결시켜 줘야 합니다.  이것은 제대로 된 동작을 위해 매우 중요한 것입니다. */
  gtk_signal_connect (GTK_OBJECT (window), "delete_event",
                      GTK_SIGNAL_FUNC (delete_event), NULL);
  gtk_container_border_width (GTK_CONTAINER (window), 10);

  /* 우리는 수평박스들을 패킹해 넣을 수직박스(vbox)를 만듭니다.저번시간에도 말씀 드렸듯이, 스택 구조를 생각하면 될 것입니다. */
  box1 = gtk_vbox_new (FALSE, 0);

  switch (which) {
  case 1:
        /* 새로운 라벨을 만듭니다. */
        label = gtk_label_new ("gtk_hbox_new (FALSE, 0);");

        /* 라벨들을 왼쪽으로 정렬시킵니다. */
        gtk_misc_set_alignment (GTK_MISC (label), 0, 0);

        /* 라벨을 수직박스(vbox box1)에 패킹합니다.  */
        gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);

        /* 라벨을 보여줍니다. */
        gtk_widget_show (label);

        /* make_box 함수를 적절한 인자로써 호출합니다. */
        box2 = make_box (FALSE, 0, FALSE, FALSE, 0);
        gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
        gtk_widget_show (box2);

        box2 = make_box (FALSE, 0, TRUE, FALSE, 0);
        gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
        gtk_widget_show (box2);

        box2 = make_box (FALSE, 0, TRUE, TRUE, 0);
        gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
        gtk_widget_show (box2);

        /* 하나의 separator를 만듭니다 */
        separator = gtk_hseparator_new ();

        /* separator를 vbox 안으로 패킹합니다.  이들 각각의 widget은 vbox 안으로 패킹되므로, 수직 방향으로 쌓일 것입니다. */
        gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
        gtk_widget_show (separator);

        /* 또다른 라벨을 만들어 그것을 보여줍니다. */
        label = gtk_label_new ("gtk_hbox_new (TRUE, 0);");
        gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
        gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
        gtk_widget_show (label);

        /* 각 인자는 homogeneous, spacing, expand, fill, padding입니다. */
        box2 = make_box (TRUE, 0, TRUE, FALSE, 0);
        gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
        gtk_widget_show (box2);

        box2 = make_box (TRUE, 0, TRUE, TRUE, 0);
        gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
        gtk_widget_show (box2);

        /* 또다른 separator */
        separator = gtk_hseparator_new ();

        /* gtk_box_pack_start 의 마지막 3가지 인자들은 expand, fill, padding 입니다. */
        gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
        gtk_widget_show (separator);

        break;

    case 2:

        /* 라벨을 새로 만든다. box1은 main()의 시작부분에서 만들어진대로 vbox이다. */
        label = gtk_label_new ("gtk_hbox_new (FALSE, 10);");
        gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
        gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
        gtk_widget_show (label);

        box2 = make_box (FALSE, 10, TRUE, FALSE, 0);
        gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
        gtk_widget_show (box2);

        box2 = make_box (FALSE, 10, TRUE, TRUE, 0);
        gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
        gtk_widget_show (box2);

        separator = gtk_hseparator_new ();
        gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
        gtk_widget_show (separator);

        label = gtk_label_new ("gtk_hbox_new (FALSE, 0);");
        gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
        gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
        gtk_widget_show (label);

        box2 = make_box (FALSE, 0, TRUE, FALSE, 10);
        gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
        gtk_widget_show (box2);

        box2 = make_box (FALSE, 0, TRUE, TRUE, 10);
        gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
        gtk_widget_show (box2);

        separator = gtk_hseparator_new ();
        gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
        gtk_widget_show (separator);
        break;

    case 3:
        /* 이것은 gtk_box_pack_end()를 이용하여 widget을 오른쪽 정렬하는 걸 보여줍니다.  먼저, 앞에서처럼 새로운 박스를 하나 만듭니다. */
        box2 = make_box (FALSE, 0, FALSE, FALSE, 0);
        /* 라벨을 하나 만듭니다. */
        label = gtk_label_new ("end");
        /* 그것을 gtk_box_pack_end()로써 패킹하므로, make_box()로 만들어진
         * hbox의 오른쪽으로 놓여지게 됩니다.
        gtk_box_pack_end (GTK_BOX (box2), label, FALSE, FALSE, 0);
        /* 라벨을 보입니다. */
        gtk_widget_show (label);

        /* box2를 box1 안으로 packing합니다. */
        gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
        gtk_widget_show (box2);

        /* bottom 쪽을 위한 separator. */
        separator = gtk_hseparator_new ();

        /* 이것은 400픽셀의 너비에 5픽셀의 높이(두께)로 separator를 세팅합니다. 이것은 우리가 만든 hbox가 또한 400픽셀의 너비이기 때문이고, "end" 라벨은 hbox의 다른 라벨들과 구분될(separated)것입니다.  그렇지 않으면, hbox 내부의 모든 widget들은 가능한만큼 서로 빽빽히 붙어서 패킹될 것입니다. */
        gtk_widget_set_usize (separator, 400, 5);

        /* main()함수의 시작부분에서 만들어진 vbox(box1)으로 separator를 패킹합니다. */
        gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
        gtk_widget_show (separator);
    }

    /* 또다른 hbox를 만듭니다. */
    quitbox = gtk_hbox_new (FALSE, 0);

    /* 우리의 quit 버튼입니다. */
    button = gtk_button_new_with_label ("Quit");

    /* 윈도를 파괴하기 시그널을 세팅합니다.  이것은 위에서 정의된 우리의 시그널 핸들러에 의해 포착될, "destroy"시그널을 윈도로 보내줍니다. */
    gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                               GTK_SIGNAL_FUNC (gtk_widget_destroy),
                               GTK_OBJECT (window));

    /* quitbox로 버튼을 패킹합니다.  gtk_box_pack_start의 마지막 세 인자는 expand, fill, padding입니다. */
    gtk_box_pack_start (GTK_BOX (quitbox), button, TRUE, FALSE, 0);
    /* vbox(box1) 안으로 quitbox를 패킹합니다. */
    gtk_box_pack_start (GTK_BOX (box1), quitbox, FALSE, FALSE, 0);

    /* 우리의 모든 widget을 포함하게 된 이 vbox를, main윈도로 패킹. */
    gtk_container_add (GTK_CONTAINER (window), box1);

    /* 그리고 남아있는 모든 것을 보여줍니다. */
    gtk_widget_show (button);
    gtk_widget_show (quitbox);

    gtk_widget_show (box1);
    /* 마지막에 윈도를 보여줘서 모든 것이 한번에 튀어나오며 보입니다. */
    gtk_widget_show (window);
    gtk_main ();

    /* gtk_main_quit()을 호출했다면 제어는 이곳으로 오게되고,  gtk_exit()를 호출하면 그렇지 않습니다. */

    return 0;
}


역시나도 재미있는것 같아요 ㅋ 저는 인터넷에서 boxpacking 예제를 참고하였어요 ^ ^ 보고 따라하기식으로 하면서 몇가지 추가하고 바꿨더니 위의 화면과는 조금 다른 것이 나오더라구요 ^ ^ 오늘 강의에 사용한 예제를 제대로 알고있다면 몇가지 바꿔보는건 매우 쉬운듯 해요 ^  ^ 아아아 ㅋㅋ 뭔가 긴 것 같지만, 반복적인 소스들, ㅋ (아! 간단하구나?)라는 생각이 들정도 ㅋ
점점 GTK+ 매력에 빠져들고 계신지요? ㅋㅋㅋㅋㅋ


ARM7의 메모리 구조

ARM7TDMI 레퍼런스 매뉴얼을 살펴보면 다음과 같이 칩코어의 블럭다이어그램을 찾을 수 있다.

 

 

위 그림에서 좌측부분을 보면 디바이스가 프로그램메모리에 접근하기 위해 PC(프로그램 카운터) 정보를 레지스터로부터 가져옴을 볼 수 있는데, 이로부터 resolve하는 어드레스의 크기는 A31:0의 총 32비트임을 보여준다. 즉, SAM7S가 인식하는 메모리의 어드레스 공간은 총 32비트(약 4.3GB)로 구성됨을 알 수 있다. 또한, PC는 32비트의 주소값을 저장해야 하므로, 아래의 그림에서 ARM7TDMI 코어의 동작모드(총 7가지)에 관계없이 레지스터15(R15)를 PC를 위해 사용한다.

 

 

SAM7S는 폰노이만 구조로서, 코어에 내장된 물리적 메모리(플래시 및 SRAM)와 각종 I/O 주변장치등을 매모리맵 I/O 방식(Memory Mapped I/O)으로 맵핑시켜 두었다. (아래 그림 참고)

 

 

 

우선 위의 그림의 좌측에서와 같이, 32비트의 어드레스 공간은 크게 3개의 영역으로 나뉘어져 있다. 하위 256MB는 물리적 메모리를 위한 영역으로 할당되어 있고, 상위 256MB는 주변장치를 위한 I/O 공간으로 할당되어 있다. 이들 사이의 3.5GB 정도의 공간은 미정의상태로서 사용되지 않는다.

 

여기서 하위 256MB의 물리적메모리를 위한 공간을 다시 자세히 살펴보면, 3개의 1MB 영역과 253MB의 미사용영역으로 이루어져 있음을 알 수 있다.(위 그림에서 우측 참고) 여기서, SAM7S의 프로그램메모리(플래시)는 0x10_0000 번지부터 맵핑되어 있으며, 데이터메모리(SRAM)는 0x20_0000 번지부터 맵핑되어 있다. 플래시와 SRAM의 공간은 각각 1MB의 영역만큼 할당되어 있으나, 플래시메모리와 SRAM의 용량은 디바이스마다 그 크기가 정해져 있기 때문에 실제 사용되는 공간은 디바이스의 메모리 용량에 의해 결정된다. (예컨데, SAM7S256의 경우 플래시메모리의 크기가 256KB이므로 256KB의 공간만큼만 사용가능)

 

디바이스가 리셋이 되면 PC는 0x0000_0000 번지로 점프하므로 이 곳은 아무런 메모리도 할당되어 있지 않아 왠지 모순이 되는 것 같다. 그러나, SAM7S에는 메모리 리맵핑(Memory Re-mapping) 기능이 제공되고 있어, 디폴트로 0x10_0000 번지의 플래시영역이 0x00_0000 번지에 리맵핑되어 있다. 따라서, 리셋이 되면 SAM7S는 플래시메모리의 내용을 읽으면서 부팅이 된다. 반면에, MC_RCR이란 레지스터의 RCB비트를 1로 세팅하면 재배치 명령(Remmap Command)이 실행되어 0x20_0000 번지의 SRAM영역이 0x00_0000 번지로 리맵핑되어 SRAM의 내용을 읽으면서 부팅이된다.

 

SAM7S의 플래시메모리는 Single Plane으로 구성되어 있기 때문에, 플래시메모리의 내용을 실행하면서 플래시-라이트를 할 수는 없다. 때문에, SAMBA와 같은 경우 이 리맵핑 기능을 이용하여 SRAM영역으로 SAMBA 코드를 복사한 뒤 SRAM영역에서 부팅하여 플래시메모리에 펌웨어를 라이팅하게 된다.


--------------------------------------------------------------------------------------

요즘 떠오르는 MCU  중의 하나 ARM7의 메모리 구조 입니다.
이번에도 오실로 스코프를 하려 했다가, 오실로 스코프가 주제가 아닌데 너무 도배를 하는 것 같아서
ARM7을 블로깅 하게 되었습니다.

위 글 중에서 중요한 것은 그림 입니다.
첫번째 그림의 경우에는 기본적인 흐름도를 나타내주고요.
두번째 그림의 경우에는 시스템의 상태별 레지스터 구조도를 알려줍니다.
참고로 썸상태의 레지스터 구조도는 그림이 없어서 참조하지 못했습니다.
마지막 그림은 플래쉬 메모리의 구조입니다.
이 세가지 그림을 잘 이해하신다면 arm7의 큰 그림은 보실 줄 알게 되는 것 입니다.
자세한 사항은 데이터 시트를 보시면 알 수 있으실 겁니다.
혹, 데이터 시트를 찾으신다면 저에게 연락주세요.^^